0xMiden / 0xMiden/protocol

Add Price Oracle Standard

Offen
#3,522 3 Kommentare 0 Reaktionen 1 zugewiesene Person Beansprucht von @onurinanc Auf GitHub ansehen
standards
Vorherrschende Sprache
Rust
Sterne
132
Forks
167
Ø Merge
1 T. 23 Std.
Gemergte PRs (30 T.)
110

Beschreibung

We would like to add a price oracle standard under `miden-standards`. The standard defines both sides:
- the `PriceFeed` account component that publishes prices
- the `price_reader` MASM library that consumes them, along with the `PriceReaderManager` component that manages its configuration.

The starting point is the `AuthMultisigSmart` oracle logic in [#2973](https://github.com/0xMiden/protocol/pull/2973). In that PR the oracle is used to normalize spending limits into USD. This proposal moves that logic into a general standard and closes three concrete issues along the way.

## Contract

The price reader on the feed side, called via FPI:

```
#! Inputs: [faucet_id_suffix, faucet_id_prefix]
#! Outputs: [is_tracked, price, exponent, timestamp]
#! Invocation: FPI
@account_procedure
pub proc get_price(faucet_id: AccountId) -> (bool, felt, felt, u32)
```

- `price` is the unit price, denominated in the quote
- `exponent` is the decimal shift, the actual value is `price * 10^(-exponent)`
- `timestamp` is the block time at which the entry was published, in seconds
- if `is_tracked = 0`, the other three fields are meaningless

On the feed side, called once at configuration time:

```
#! Inputs: []
#! Outputs: [QUOTE_ID]
#! Invocation: FPI
@account_procedure
pub proc get_quote_id() -> word
```

The API the consumer sees on the reader side:

```
#! Inputs: [ASSET, config_slot_prefix, config_slot_suffix,
#! price_keys_slot_prefix, price_keys_slot_suffix]
#! Outputs: [is_tracked, value_in_quote]
pub proc quote_asset_value(
asset: Asset,
config_slot: StorageSlotName,
price_keys_slot: StorageSlotName,
) -> (bool, felt)
```

Extracts the faucet id and amount from the asset, resolves the key the feed uses for that asset, performs an FPI to the feed, validates `timestamp` against `max_age_secs`, normalizes `price` to `quote_exponent`, and multiplies by `amount`.

Key resolution:

```
#! Inputs: [FAUCET_ID_WORD, price_keys_slot_prefix, price_keys_slot_suffix]
#! Outputs: [FEED_PRICE_KEY]
proc resolve_feed_price_key(faucet_id: AccountId, price_keys_slot: StorageSlotName) -> word
```

If there is no entry in the map, the faucet id itself is returned.

We separate the identities by name because they cause conceptual confusion:

- `FEED_ACCOUNT_ID`: the `AccountId` of the oracle account to be called via FPI
- `FEED_PRICE_KEY`: the key under which an asset's price is stored within that oracle

The standard's own feed uses the faucet `AccountId` directly as the `FEED_PRICE_KEY`. For feeds that publish under a different key, the `price_keys` map comes into play.

## Quote and scale

`QUOTE_ID` is a symbolic word, for example ASCII-packed `"USD"`. Making it a faucet `AccountId` doesn't work because USD has no faucet on Miden, if pricing in a stablecoin is desired, that faucet's id can be placed in the same word.

`quote_exponent` is the fixed scale of the running sum. The feed returns its own `exponent` for each asset, and the reader normalizes all of them to the scale in the config.

## Staleness

Responsibility is split across the two sides, because there are two distinct threats.

- The feed side applies its own expiration delta. At the start of `get_price`, `tx::update_expiration_block_delta` is called through a standard helper procedure, and the delta is defined as a constant of the component itself.
- The reader side compares the returned `timestamp` against `tx::get_block_timestamp` and panics with `ERR_PRICE_STALE` if `now - timestamp > max_age_secs`.

## Storage layout

The reader takes the names of two map slots as parameters, so it isn't tied to any particular storage layout.

`config_slot`, reserved keys:

- `CONFIG_KEY_FEED_ACCOUNT_ID` → `[prefix, suffix, 0, 0]`
- `CONFIG_KEY_GET_PRICE_PROC_ROOT` → `GET_PRICE_PROC_ROOT`
- `CONFIG_KEY_QUOTE` → `QUOTE_ID`
- `CONFIG_KEY_PARAMS` → `[quote_exponent, max_age_secs, untracked_policy, 0]`

`price_keys_slot`:

- `FAUCET_ID_WORD` → `FEED_PRICE_KEY`

`untracked_policy` takes two values: 0 omit (not added to the USD total), 1 reject (fail-closed, panic).

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.