Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 25 additions & 18 deletions src/lib/op/LibOpFtsoCurrentPricePair.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ library LibOpFtsoCurrentPricePair {

/// Extern implementation for the process of converting two symbols to a
/// derived price via their respective FTSOs.
/// This works by fetching the price of each symbol from its respective FTSO
/// and then dividing the two prices to get the derived price. All the same
/// considerations apply as for `ftsoCurrentPriceUsd` for each price fetch,
/// e.g. stale and non-finalized prices are rejected, etc.
/// This works by fetching the USD price of each symbol from its respective
/// FTSO and then dividing the numerator price by the denominator price.
/// All the same considerations apply as for `ftsoCurrentPriceUsd` for each
/// price fetch, e.g. stale and non-finalized prices are rejected, etc.
/// Note that as the price is derived from two FTSOs, it is not a literal
/// value that any FTSO is reporting, rather it is calculated from separate
/// values. Notably, and especially if the timeout is long, the two prices
Expand All @@ -32,14 +32,19 @@ library LibOpFtsoCurrentPricePair {
/// individual quotes, so SHOULD NOT be relied upon for high precision
/// calculations.
/// @param inputs The inputs to the operation.
/// 0. The symbol of the first asset to fetch the price of, encoded as an
/// unwrapped `IntOrAString` (i.e. a `uint256`).
/// 1. The symbol of the second asset to fetch the price of, encoded as an
/// unwrapped `IntOrAString` (i.e. a `uint256`).
/// 0. symbolA — the numerator asset symbol, encoded as an unwrapped
/// `IntOrAString` (i.e. a `uint256`).
/// 1. symbolB — the denominator asset symbol, encoded as an unwrapped
/// `IntOrAString` (i.e. a `uint256`).
/// 2. The timeout in seconds to invalidate prices after if the FTSO stops
/// updating for some time.
/// @return outputs The outputs of the operation.
/// 0. The derived price of the two assets, normalized to 18 decimals.
/// 0. The derived price symbolA/symbolB, normalized to 18 decimals.
/// @dev Reverts with `InactiveFtso`, `StalePrice`, or `PriceNotFinalized`
/// (propagated from `ftsoCurrentPriceUsd`) if either FTSO is unusable.
/// The denominator (symbolB) is fetched first; errors on that leg abort
/// before the numerator fetch. Reverts with `DivisionByZero` (from
/// `LibDecimalFloat`) if the denominator USD price is zero.
function run(OperandV2 operand, StackItem[] memory inputs) internal view returns (StackItem[] memory) {
uint256 symbolA;
assembly ("memory-safe") {
Expand All @@ -49,25 +54,27 @@ library LibOpFtsoCurrentPricePair {
symbolA := mload(inputs)
mstore(inputs, 2)
}
StackItem[] memory outputsB = LibOpFtsoCurrentPriceUsd.run(operand, inputs);
// symbolB (denominator) is fetched first via the memory-truncation trick
// above, which exposes [symbolB, timeout] as a 2-element inputs array.
StackItem[] memory denominatorOutputs = LibOpFtsoCurrentPriceUsd.run(operand, inputs);
assembly ("memory-safe") {
mstore(add(inputs, 0x20), symbolA)
}
StackItem[] memory outputsA = LibOpFtsoCurrentPriceUsd.run(operand, inputs);
StackItem[] memory numeratorOutputs = LibOpFtsoCurrentPriceUsd.run(operand, inputs);

Float priceA;
Float priceB;
Float numeratorPrice;
Float denominatorPrice;
assembly ("memory-safe") {
priceA := mload(add(outputsA, 0x20))
priceB := mload(add(outputsB, 0x20))
numeratorPrice := mload(add(numeratorOutputs, 0x20))
denominatorPrice := mload(add(denominatorOutputs, 0x20))
}

Float pricePair = priceA.div(priceB);
Float pricePair = numeratorPrice.div(denominatorPrice);

// Repurpose one of the inner outputs arrays to return the derived price.
assembly ("memory-safe") {
mstore(add(outputsA, 0x20), pricePair)
mstore(add(numeratorOutputs, 0x20), pricePair)
}
return outputsA;
return numeratorOutputs;
}
}
Loading