read()
and peek()
interfaces) via the poke()
method; the read()
and peek()
methods will give the current value of the price feed, and other contracts must be whitelisted in order to call these. An OSM contract can only read from a single price feed, so in practice one OSM contract must be deployed per collateral type.stopped
: flag (uint256
) that disables price feed updates if non-zerosrc
: address
of DSValue that the OSM will read fromONE_HOUR
: 3600 seconds (uint16(3600)
)hop
: time delay between poke
calls (uint16
); defaults to ONE_HOUR
zzz
: time of last update (rounded down to nearest multiple of hop
)cur
: Feed
struct that holds the current price valuenxt
: Feed
struct that holds the next price valuebud
: mapping from address
to uint256
; whitelists feed readersusr
such that wards[usr] == 1
).rely
/deny
: add or remove authorized users (via modifications to the wards
mapping)stop()
/start()
: toggle whether price feed can be updated (by changing the value of stopped
)change(address)
: change data source for prices (by setting src
)step(uint16)
: change interval between price updates (by setting hop
)void()
: similar to stop
, except it also sets cur
and nxt
to a Feed
struct with zero valueskiss(address)
/diss(address)
: add/remove authorized feed consumers (via modifications to the buds
mapping)usr
such that buds[usr] == 1
):peek()
: returns the current feed value and a boolean indicating whether it is validpeep()
: returns the next feed value (i.e. the one that will become the current value upon the next poke()
call), and a boolean indicating whether it is validread()
: returns the current feed value; reverts if it was not set by some valid mechanismpoke()
: updates the current feed value and reads the next oneFeed
struct: a struct with two uint128
members, val
and has
. Used to store price feed data.poke()
method to update the current price and read the next price. The contract tracks the time of the last call to poke()
in the zzz
variable (rounded down to the nearest multiple of hop
; see Failure Modes for more discussion of this), and will not allow poke()
to be called again until block.timestamp
is at least zzz+hop
. Values are read from a designated DSValue contract (its address is stored in src
). The purpose of this delayed updating mechanism is to ensure that there is time to detect and react to an Oracle attack (e.g. setting a collateral's price to zero). Responses to this include calling stop()
or void()
, or triggering Emergency Shutdown.cur
value via the peek()
and read()
methods (peek()
returns an additional boolean indicating whether the value has actually been set; read()
reverts if the value has not been set). The nxt
value may be inspected via peep()
.wards
may start and stop, set the src
, call void()
, and add new readers; addresses mapped to 1 in buds
may call peek()
, peep()
, and read()
.peek()
for peep()
(or vice-versa)peep()
is called where instead peek()
should be used. A mnemonic to help distinguish them: "since 'k' comes before 'p' in the English alphabet, the value returned by peek()
comes before the value returned by peep()
in chronological order". Or: "peek()
returns the kurrent value".poke()
is not called promptly, allowing malicious prices to be swiftly uptakenpoke()
is always callable as soon as block.timestamp / hop
increments, regardless of when the last poke()
call occurred (because zzz
is rounded down to the nearest multiple of hop
). This means the contract does not actually guarantee that a time interval of at least hop
seconds has passed since the last poke()
call before the next one; rather this is only (approximately) guaranteed if the last poke()
call occurred shortly after the previous increase of block.timestamp / hop
. Thus, a malicious price value can be acknowledged by the system in a time potentially much less than hop
.hop
is 1 hour, so checks must be performed at the top of the hour)poke
is public, and thus callable by anyone, helps mitigate concerns, though it does not eliminate them. For example, network congestion could prevent anyone from successfully calling poke()
for a period of time. If an MKR holder observes that poke
has not been promptly called, the actions they can take include:poke()
themselves and decide if the next value is malicious or notstop()
or void()
(the former if only nxt
is malicious; the latter if the malicious value is already in cur
)poke()
calls in a short time window each hop
period).src
to either a malicious contract or to something that lacks a peek()
interface, causing transactions that poke()
the affected OSM to revertstop
and void
inappropriatelywards
of the OSM.