0xMiden / 0xMiden/protocol

Add Price Oracle Standard

Đang mở
#3,522 3 bình luận 0 reaction 1 người được giao Được @onurinanc nhận Xem trên GitHub
standards
Ngôn ngữ chính
Rust
Star
132
Fork
167
Merge trung bình
1 ngày 23 giờ
Pull request đã merge (30 ngày)
110

Mô tả

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

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.