0xMiden / 0xMiden/protocol

Add Price Oracle Standard

Abierto
#3,522 3 comentarios 0 reacciones 1 asignado Reclamado por @onurinanc Ver en GitHub
standards
Lenguaje dominante
Rust
Estrellas
132
Forks
167
Merge medio
1 d 23 h
PR fusionados (30 d)
110

Descripción

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).

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.