Both policies are written in Vyper 0.4.3 and import snekmate. YBTwocryptoPolicy is verified on Blockscout. The source of YBOraclizedPolicy is in curvefi/twocrypto-ng, branch oraclized-policy, file contracts/main/YBOraclizedPolicy.vy, commit 87dbb0d1a269e50f21757c0c1786e620fcaae5a2.
Pool policy
This page is for developers who integrate with or audit a YieldBasis pool. It describes the policy hook of a Twocrypto v3.0.0 pool and the YieldBasis pool policies. A pool policy is a contract that the pool calls to get its swap fee and its price_scale target. The pool also pushes its state to the policy after each operation. For the concept and the LP view, see Pool policies.
The pools of markets 7 to 10 run Twocrypto v3.0.0 and have this hook. The pools of markets 0 to 6 run older pool code without a policy hook. On those pools, a call to POLICY() reverts.
The YieldBasis pool policy is YBOraclizedPolicy, the oracle-report policy. It prices swaps against signed price reports. Some pools can still run the earlier YBTwocryptoPolicy, which uses only the pool's own prices.
To see which policy a pool runs, see Contract addresses or call POLICY() on the pool. A return of empty(address) means that the pool has no policy.
Pool side: the policy hook
Interface the pool calls
interface Policy:
def get_fee(xp: uint256[N_COINS]) -> uint256: view
def get_price_scale() -> uint256: view
def update_pool_state(xp: uint256[N_COINS],
price_scale: uint256,
price_oracle: uint256,
last_prices: uint256,
virtual_price: uint256,
xcp_profit: uint256,
D: uint256,
oracle_timestamp: uint256): nonpayable
The pool stores the policy in POLICY: public(Policy). The value is empty(address) at deployment. A pool that the factory deploys in initialization mode can attach a policy in its one-time initialize() call, before the first deposit. The pool deployer or factory.admin() can call initialize(), so the deployer can attach any policy without a vote. After that, only set_policy_contract changes the policy.
get_fee
The pool fee function _fee(xp) calls POLICY.get_fee(xp) when the pool has a policy. A return of 0 selects the pool's native fee (mid_fee, out_fee, fee_gamma). The pool clamps any other return to [MIN_FEE, MAX_FEE] = [10^5, 10^10] in fee units (0.1 bp to 100%).
Swaps and liquidity operations use _fee. For add_liquidity and imbalanced removals, _calc_token_fee charges _fee(xp) / 2 × Sdiff / S of the LP amount, plus NOISE_FEE. Sdiff / S is 0 for a balanced operation and 1 for a one-coin operation. A deposit during the donation protection period also pays a spam penalty of at most _fee(xp) / 2.
Balanced remove_liquidity pays no fee. Donations pay only NOISE_FEE (0.1 bp). The view fee() evaluates _fee at the stored balances.
xp is the balance vector after the operation and before its fee, with coin1 scaled by price_scale.
get_price_scale
tweak_price calls POLICY.get_price_scale() only when both conditions are true: vp_boosted > lp_xcp_profit (profit gate), and block.timestamp > last_timestamp. The pool reads last_timestamp before it stores the new timestamp, so only the first operation in a block that calls tweak_price can move price_scale. That operation can be a swap, an unbalanced liquidity operation, or a donation. If it fails the profit gate, price_scale does not move in that block.
target_price: uint256 = price_oracle
if p_policy > 0:
policy_bound: uint256 = unsafe_div(price_oracle, 5)
target_price = min(
max(p_policy, unsafe_sub(price_oracle, policy_bound)),
price_oracle + policy_bound,
)
norm = |target_price / price_scale - 1|
adjustment_step = min(norm / 5, adjustment_step_max)
# p_new moves price_scale by adjustment_step toward target_price,
# only if adjustment_step > adjustment_step_min
A return of 0 selects the native target, price_oracle. The pool moves one fifth of the gap, so both policies return price_scale ± 5 × move.
The pool moves price_scale only when all of these conditions are true:
- The profit gate passes, and the call is the first
tweak_pricein the block. - The relative step (
adjustment_step) is aboveadjustment_step_min. - The new virtual price stays above
10**18and at or abovelp_xcp_profit.
The pool also limits the move in two ways. It clamps the target to price_oracle ± 20%, and it caps the relative step at adjustment_step_max. In these two cases, price_scale moves by less than move. move is a price amount, and the two step limits are fractions of price_scale.
update_pool_state
The pool calls _update_policy_state after each state change. It ABI-encodes the arguments with self.last_timestamp as oracle_timestamp and makes a raw_call.
| Call site | must_succeed | Effect of a policy revert |
|---|---|---|
First add_liquidity into an empty pool (initialization) | True | Operation reverts |
tweak_price (swaps, add_liquidity including donations, remove_liquidity_fixed_out, remove_liquidity_one_coin) | True | Operation reverts |
set_policy_contract (attach, if D > 0) | True | Attach reverts |
remove_liquidity (balanced, if amount > 0) | False, 300,000 gas | Ignored, if at least 500,000 gas remained before the call. Otherwise the pool reverts. |
LT.withdraw uses remove_liquidity_fixed_out, so a reverting policy blocks it. LT.emergency_withdraw uses balanced remove_liquidity. A failed policy call does not block it if the transaction has at least 500,000 gas left before the policy call.
Attach, replace or remove
@external
@nonreentrant
def set_policy_contract(policy: Policy)
- Access:
msg.sender == factory.admin()of the Curve Twocrypto factory, else revertonly owner. policymust not becoins[0]orcoins[1](dev revert).empty(address)removes the policy.- If
D > 0, the pool pushes its state withmust_succeed = True. - Event:
SetPolicyContract(policy).
For the YieldBasis pools, factory.admin() is the owner proxy 0x97aA696e37659Fb4f0B53824246d802Df40E980A. Its execute() accepts calls only from its dao, the Curve Ownership Agent 0x40907540d8a6C65c637785e8f8B742ae6b0b9968. After initialization, a policy change therefore needs a Curve ownership vote (voting contract 0xE478de485ad2fE566d49342Cbd03E49ed7DB3356).
The emergency DAO of the proxy can call only emergency_parameters. That function calls apply_new_parameters(mid_fee, out_fee, fee_gamma, adjustment_step_min, adjustment_step_max, ma_time) on the pool. The emergency DAO cannot call set_policy_contract. To read the two roles, call dao() and emergency_dao() on the proxy.
The YieldBasis Factory.add_market() does not check the pool code version or the policy.
YBTwocryptoPolicy
YBTwocryptoPolicy, the earlier YieldBasis policy, is a price_scale controller with two exponential moving averages (EMAs), one fast and one slow. It reads no external price, has no owner, and has no setters.
Constants and immutables
| Name | Value |
|---|---|
N_COINS | 2 |
PRECISION | 10^18 |
TWEAK_PRICE_MULTIPLIER | 5 |
CAP_RAMP_SECONDS | 3,600 |
POOL | immutable address |
FAST_HALF_LIFE, SLOW_HALF_LIFE | immutable, seconds |
KAPPA | immutable, 1e18 = 1.0 |
DEADBAND, MIN_CAP, MAX_CAP | immutable, relative WAD (1e18 = 100%) |
Storage
struct State:
last_update_ts: uint256
last_prices: uint256
fast_ema: uint256
slow_ema: uint256
price_scale: uint256
state: public(State)
Constructor
@deploy
def __init__(pool: address, fast_half_life: uint256, slow_half_life: uint256,
kappa: uint256, deadband: uint256, min_cap: uint256, max_cap: uint256)
| Check | Revert string |
|---|---|
pool != empty(address) | pool=0 |
600 ≤ fast_half_life ≤ 604,800 | fast half-life |
600 ≤ slow_half_life ≤ 604,800 | slow half-life |
fast_half_life ≤ slow_half_life | half-life order |
kappa ≤ 2e18 | kappa |
deadband ≤ 60 bp | deadband |
max_cap ≤ 60 bp | max cap |
1 bp ≤ min_cap ≤ max_cap | min cap |
External functions
| Signature | Mutability | Access | Returns |
|---|---|---|---|
get_fee(_xp: uint256[2]) -> uint256 | pure | Anyone | Always 0 (native pool fee) |
get_emas() -> uint256[2] | view | Anyone | Projected [fast, slow], or [0, 0] before the attach callback |
get_price_scale() -> uint256 | view | Anyone | Target for the pool, or 0 before the attach callback |
update_pool_state(_xp, price_scale, price_oracle, last_prices, _virtual_price, _xcp_profit, _D, _oracle_timestamp) | nonpayable | POOL only, else auth! | — |
state() -> State | view | Anyone | Stored snapshot |
POOL(), FAST_HALF_LIFE(), SLOW_HALF_LIFE(), KAPPA(), DEADBAND(), MIN_CAP(), MAX_CAP() | view | Anyone | Immutables |
The contract emits no events.
EMA and target
Each update_pool_state settles both EMAs toward the last_prices stored at the previous callback, then stores the snapshot of this callback. The attach callback seeds both EMAs with price_oracle.
get_price_scale():
raw_target = slow + KAPPA · (fast − slow), floored at 0.gap = |raw_target − price_scale|. Ifgap ≤ DEADBAND · price_scale, returnprice_scale(hold).cap = MIN_CAP + (MAX_CAP − MIN_CAP) · min(t − last_update_ts, 3600) / 3600.move = min(gap, price_scale · cap).- Return
price_scale ± 5 · move.
Parameters of the deployed policies
The YBTwocryptoPolicy contracts that Curve vote 1494 attached use the same immutable parameters:
| Parameter | Value |
|---|---|
FAST_HALF_LIFE | 8,100 s |
SLOW_HALF_LIFE | 57,600 s |
KAPPA | 1.30 |
DEADBAND | 8 bp |
MIN_CAP | 12 bp |
MAX_CAP | 54 bp |
Governance history:
- Curve votes 1486 (ended 17 August 2026, executed) and 1491 (ended 29 August 2026, executed) attached the first
YBTwocryptoPolicycontracts. - These first policies used fast 7,500 s, slow 54,000 s,
KAPPA1.27,DEADBAND2.8 bp,MIN_CAP12 bp, andMAX_CAP48 bp. - Curve vote 1494 (ended 7 September 2026, executed) replaced them with policies that use the parameters above.
YBOraclizedPolicy
YBOraclizedPolicy is the YieldBasis pool policy. The team's forum post of 21 October 2026 explains the design and its backtest.
YBOraclizedPolicy prices swap fees from signed Pyth Pro (Pyth Lazer) price reports. It sets a trend-gated dual-EMA price_scale target. It also checks deposits into LT (the yb-LP vault) against the debt ratio of the leveraged AMM (LEVAMM).
Units
- Fees:
FEE_PRECISION= 10^10 = 100%. - Prices and EMAs: 18 decimals. A price report prices coin1 in USD (1e18 = $1). coin0 (crvUSD) counts as $1.
- Relative values: WAD, 1e18 = 100%.
- Pyth times: microseconds.
Constants
| Name | Value | Meaning |
|---|---|---|
N_COINS | 2 | |
PRECISION | 10^18 | |
FEE_PRECISION | 10^10 | |
MIN_FEE | 10^5 | 0.1 bp, the pool's minimum |
A_MULTIPLIER | 10,000 | |
TWEAK_PRICE_MULTIPLIER | 5 | |
PYTH_PAYLOAD_MAGIC | 2,479,346,549 (0x93c7d375) | Pyth Lazer payload format |
MIN_REPORT_PRICE | 10^6 | $1e-12 |
MAX_REPORT_PRICE | 10^30 | $1e12 |
MAX_PAST_WINDOW_US | 12,000,000 | 12 s |
MAX_FUTURE_WINDOW_US | 6,000,000 | 6 s |
Immutables
| Name | Type | Example: policy 0xAEa9…8A7d (WETH market) |
|---|---|---|
POOL | address | 0x656341Ef90b622c6634e0573772FfB7f3669b9f3 |
AMM | address | 0x5f8D24F33Cc5a1D5D1bf012261E6A2214C92233c (LEVAMM) |
FEED_ID | uint32 | 2 (ETH/USD) |
EMERGENCY_ADMIN | address | 0x467947EE34aF926cF1DCac093870f613C96B1E0c (Safe) |
Config structs
struct OracleConfig:
verifier: address # Pyth Lazer verifier
channel: uint8 # 1 real_time, 2 fixed_rate@50ms, 3 fixed_rate@200ms, 4 fixed_rate@1000ms
past_window_us: uint256 # oldest admissible observation, before block.timestamp
future_window_us: uint256 # newest admissible message, after block.timestamp
min_price: uint256 # 1e18 = $1
max_price: uint256
struct FeeConfig:
base_fee: uint256 # FEE_PRECISION
capture: uint256 # PRECISION
fallback_fee: uint256 # FEE_PRECISION
relax_time: uint256 # seconds
struct DriverConfig:
fast_half_life: uint256 # seconds
slow_half_life: uint256 # seconds
trend_gate: uint256 # WAD
step_cap: uint256 # WAD
report_lifetime: uint256 # seconds; 0 = never observe reports
struct PolicyConfig:
fee: FeeConfig
driver: DriverConfig
ratio_tolerance: uint256 # WAD
struct Report:
asset_price: uint256 # 0 before any report and after a kill
observation_us: uint256 # replay watermark, kept on a kill
struct State:
last_update_ts: uint256
fast_ema: uint256
slow_ema: uint256
price_scale: uint256
xp: uint256[2]
D: uint256
last_prices: uint256 # pool spot after the last callback's operation
Public storage: config, oracle, is_killed, report, state, cached_A, cached_supply, amm_surplus.
Deployment configuration (example)
The deployer gave the policy 0xAEa95d551154443E73565bE724d59D63d72f8A7d of the WETH market this configuration. The owner can change it. To read the current values, call config() and oracle().
| Field | Raw | Meaning |
|---|---|---|
fee.base_fee | 50,000,000 | 0.50% |
fee.capture | 0.675e18 | 67.5% |
fee.fallback_fee | 175,000,000 | 1.75% |
fee.relax_time | 300 | 300 s |
driver.fast_half_life | 4,800 | 80 minutes |
driver.slow_half_life | 9,600 | 160 minutes |
driver.trend_gate | 0.007e18 | 0.70% |
driver.step_cap | 0.005e18 | 0.50% |
driver.report_lifetime | 5,400 | 90 minutes |
ratio_tolerance | 1e14 | 1 bp |
oracle.verifier | 0xACeA761c27A909d4D3895128EBe6370FDE2dF481 | Pyth Lazer verifier |
oracle.channel | 4 | fixed_rate@1000ms |
oracle.past_window_us | 12,000,000 | 12 s |
oracle.future_window_us | 6,000,000 | 6 s |
oracle.min_price / max_price | 100e18 / 100,000e18 | $100 / $100,000 |
The verifier 0xACeA…F481 is an ERC-1967 proxy for the Pyth PythLazer contract. To find its implementation, read the ERC-1967 implementation slot of the proxy.
Constructor
@deploy
def __init__(pool: address, amm: address, feed_id: uint32, emergency_admin: address,
oracle: OracleConfig, config: PolicyConfig)
| Check | Revert string |
|---|---|
pool != empty(address) | pool=0 |
feed_id > 0 | feed id |
emergency_admin != empty(address) | emergency admin=0 |
LevAMM(amm).COLLATERAL() == pool | amm collateral |
LevAMM(amm).STABLECOIN() == pool.coins(0) | amm stablecoin |
pool.precisions()[0] == 1 | pool precision |
pool.admin_fee() == 0 | admin fee |
The constructor caches pool.A(), makes the deployer the owner, then applies oracle and config with the setter checks below.
External functions
| Signature | Mutability | Access |
|---|---|---|
get_fee(xp: uint256[2]) -> uint256 | view | Anyone (called by the pool) |
get_fee_with_report(xp: uint256[2], report_price: uint256, report_age_us: uint256) -> uint256 | view | Anyone |
get_price_scale() -> uint256 | view | Anyone (called by the pool) |
get_emas() -> uint256[2] | view | Anyone |
update_pool_state(xp: uint256[2], price_scale: uint256, price_oracle: uint256, last_prices: uint256, _virtual_price: uint256, _xcp_profit: uint256, D: uint256, _oracle_timestamp: uint256) | nonpayable | POOL only, else auth! |
sync() | nonpayable | Anyone |
deliver_oracle_report(report_data: Bytes[112]) | payable, nonreentrant | Anyone |
verification_fee() -> uint256 | view | Anyone |
set_policy_config(config: PolicyConfig) | nonpayable | Owner |
set_oracle_config(oracle: OracleConfig) | nonpayable | Owner |
set_killed(is_killed: bool) | nonpayable | Owner or EMERGENCY_ADMIN, else access |
get_dy_with_report(i: uint256, j: uint256, dx: uint256, report_data: Bytes[112], block_timestamp: uint256) -> uint256 | view | Anyone |
get_dy_with_report_age(i: uint256, j: uint256, dx: uint256, report_price: uint256, report_age_us: uint256) -> uint256 | view | Anyone |
owner() -> address, transfer_ownership(new_owner: address), renounce_ownership() | snekmate ownable | Owner only, for transfer_ownership and renounce_ownership |
config(), oracle(), is_killed(), report(), state(), cached_A(), cached_supply(), amm_surplus(), POOL(), AMM(), FEED_ID(), EMERGENCY_ADMIN() | view | Anyone |
In snekmate ownable, a call to an owner function from another address reverts with ownable: caller is not the owner. transfer_ownership(new_owner) reverts with ownable: new owner is the zero address if new_owner is the zero address.
Fee
get_fee(xp) evaluates _get_fee against the stored report and its age. get_fee_with_report does the same with a caller-supplied price and age, without verification.
_get_fee(xp, report_price, report_age_us):
- If killed, return 0 (native pool fee).
- If neither balance fell, or neither balance rose, against
state.xp, returnfallback_fee. This covers every liquidity operation. - If
report_price == 0orreport_age_us ≥ relax_time · 10^6, returnfallback_fee. - If
state.D == 0orpool.D() != state.D, returnfallback_fee. This happens before the attach callback, after a failed best-effort removal callback, and usually during an A ramp. - Compute the edge. If it is 0, return
fallback_fee. - Compute the reported fee. Return it only if it is below
fallback_feeand the swap is corrective. Else returnfallback_fee.
Edge. With coin_in the coin whose balance rose:
The policy values both legs in coin0. It multiplies a coin1 delta in xp by report_price / price_scale. The edge is 0 when the input is worth at least the output.
Reported fee.
The + 1 fee unit applies only when age > 0 and fresh < fallback.
Corrective test. The marginal price get_p(xp, D, [cached_A, 0]) · price_scale must move toward report_price and not pass it. A swap that empties a balance is not corrective.
The constructor and sync() set cached_A. During an A ramp, quotes and swaps can disagree on the fee. The source comment gives two options: kill the policy for the ramp, or call sync() after the pool checkpoints its final A.
price_scale driver
Observation (_observation):
state.last_prices is the pool spot stored at the previous callback, so an operation cannot steer the EMAs with its own trade.
EMAs (_project_emas, _ema): the same true half-life EMA as YBTwocryptoPolicy, with dt = block.timestamp − state.last_update_ts. If dt == 0, the function returns the stored EMAs.
Target (get_price_scale):
If trend_gate == 0, the target is fast.
The function returns 0 while killed or before the attach callback. YBOraclizedPolicy has no deadband, no KAPPA and no cap ramp.
Pool callback and LT deviation check
update_pool_state (POOL only):
- If not killed, run the LT deviation check against the previous snapshot. A revert unwinds the operation.
- Settle the EMAs with
_observation(snapshot). The attach callback seeds both EMAs withprice_oracle. - Store
State(block.timestamp, fast, slow, price_scale, xp, D, last_prices).
LT deviation check (_update_lt_gate(stable, old_stable)):
- If
pool.user_supply()is unchanged (and not 0), return. Swaps and donations do not change user supply. - Store the supply in
cached_supply. If supply fell (an exit), return. surplus = pool.balanceOf(AMM) − AMM.collateral_amount(). This is LP that the LEVAMM holds beyond its booked collateral.- If
old_supply == 0, record only. If the LEVAMM book is empty, the policy resets the surplus to 0. - Else, if
surplus ≥ amm_surplus + added_lp, the add is an LT deposit. Ifcollateral != 0, the check below must pass, else the call reverts withLT deviation.
added_lp is the growth of pool.user_supply(). added_debt is the growth of xp[0] since the previous callback.
The check is exact only while the pool admin_fee is 0. The constructor checks this. If governance later sets a pool admin fee, an exact deposit can fail.
The check applies to each LT.deposit into the market of the pool while the policy is not killed and the LEVAMM has collateral. HybridVault.deposit and LTMigrator pass a debt that the caller or the migration chooses. They revert with LT deviation if that debt is more than ratio_tolerance off the LEVAMM ratio.
sync() re-reads A, the snapshot geometry (xp, D, price_scale), cached_supply, and amm_surplus. It does not settle the EMAs. Before the attach callback, it updates only cached_A and amm_surplus.
sync() reverts inside a pool operation, because pool.price_scale() is nonreentrant. It reverts with pool uninitialized if the pool price_scale is 0. Call it after an A ramp ends, after a failed balanced-removal callback, or after an LP gift to the LEVAMM.
Price report admission
deliver_oracle_report(report_data). Send verification_fee() as msg.value.
- Revert
killedwhile killed. - Revert
pool uninitializedifpool.price_scale() == 0. This call also blocks delivery from inside a pool operation. len(report_data) == 112, elsereport length.- Call
verifier.verifyUpdate(report_data)withmsg.value. The signer must not be 0 (signer). The payload must be 41 bytes (payload length). - Decode the payload (below).
- Do the time checks:
0 < observation ≤ message ≤ block_time + future_window, elsereport time.observation + past_window ≥ block_time, elsereport too old.observation > report.observation_us, elseobservation not newer.
min_price ≤ price ≤ max_price(price bounds).- Store the report and emit
ReportAccepted.
Payload layout (big-endian):
| Bytes | Field | Required value | Revert |
|---|---|---|---|
| [0:4] | magic | 0x93c7d375 | payload magic |
| [4:12] | message time, µs | — | — |
| [12] | channel | oracle.channel | channel |
| [13] | feed count | 1 | one feed |
| [14:18] | feed id | FEED_ID | feed |
| [18:20] | property count, property id | 3, 0 (price) | price property |
| [20:28] | price mantissa, int64 | > 0 | price/exponent |
| [28] | property id | 4 (exponent) | exponent property |
| [29:31] | exponent, int16 | −18 to 0 | price/exponent |
| [31:33] | property id, value present | 12 (feed update time), 1 | observation property |
| [33:41] | observation time, µs | — | — |
Price = mantissa · 10^(18 + exponent).
Report age is block_time − observation, or 0 if the observation is after the block time.
Send the exact fee: read verification_fee() in the same transaction. With the current verifier implementation, the verifier refunds any excess over verification_fee to msg.sender (the policy) with transfer. The policy cannot receive ETH, so the refund fails. An overpayment therefore reverts the whole delivery without a reason string. The verifier is an upgradeable proxy, so a later implementation can behave differently.
Quote views
get_dy_with_report and get_dy_with_report_age return pool.get_dy(i, j, dx) as if the policy stored the given report. They decode the report but do not verify the signature or admit it. get_dy_with_report takes the report age at block_timestamp, so pass the timestamp of the block you target.
_quote reverts with coin index out of range, not attached (when pool.POLICY() != self), or unsafe value for y. It re-solves D during an A ramp. While the policy is killed, it uses pool.fee_calc(xp).
Governance
set_policy_config(config) (owner) replaces the whole config. It does not checkpoint the EMAs. Changed half-lives apply to the whole interval since the last pool operation. A higher relax_time or report_lifetime can revive a stored report.
| Field | Bound | Revert |
|---|---|---|
base_fee | MIN_FEE ≤ base_fee ≤ fallback_fee | base fee |
fallback_fee | ≤ 10^9 (10%) | fallback fee |
capture | ≤ 1e18 | capture |
relax_time | 1 to 3,600 s | relax time |
fast_half_life | ≥ 600 s and ≤ slow_half_life | fast half-life |
slow_half_life | ≤ 604,800 s | slow half-life |
trend_gate | ≤ 0.1e18 (10%) | trend gate |
step_cap | > 0 and ≤ 60 bp | step cap |
report_lifetime | ≤ 14,400 s | report lifetime |
ratio_tolerance | ≤ 1e15 (10 bp) | ratio tolerance |
set_oracle_config(oracle) (owner) sets the verifier, channel, windows, and price bounds. Each call sets report.asset_price to 0 and keeps report.observation_us as the replay watermark.
| Field | Bound | Revert |
|---|---|---|
verifier | Has code | verifier |
channel | 1 to 4 | channel |
past_window_us | ≤ 12,000,000 | past window |
future_window_us | ≤ 6,000,000 | future window |
min_price, max_price | 10^6 ≤ min ≤ max ≤ 10^30 | price bounds |
The verifier decides which prices are authentic. The owner can set any contract as verifier, so the owner holds that trust.
set_killed(is_killed) (owner or EMERGENCY_ADMIN):
- Kill: sets
report.asset_priceto 0 and keeps the watermark. While killed,get_feeandget_price_scalereturn 0, reports revert withkilled, and the LT deviation check is off. Callbacks still settle the EMAs. - Revive: calls
sync(). - Emits
SetKilled.
The owner of the policy 0xAEa9…8A7d is the YB DAO 0x42F2A41A0D0e65A440813190880c8a65124895Fa. To check, call owner().
Events
| Event | Fields | Emitted by |
|---|---|---|
ReportAccepted | asset_price: uint256, observation_us: uint256, message_us: uint256 | deliver_oracle_report |
SetPolicyConfig | config: PolicyConfig | Constructor, set_policy_config |
SetOracleConfig | oracle: OracleConfig | Constructor, set_oracle_config |
SetKilled | is_killed: bool | set_killed |
snekmate ownable also emits its ownership-transfer event.
Revert strings
| String | Function |
|---|---|
auth! | update_pool_state |
LT deviation | update_pool_state (LT deviation check) |
pool uninitialized | sync, set_killed(False), deliver_oracle_report |
killed | deliver_oracle_report |
report length, signer, payload length | deliver_oracle_report |
payload magic, channel, one feed, feed, price property, exponent property, observation property, price/exponent | deliver_oracle_report, get_dy_with_report |
report time, report too old, observation not newer, price bounds | deliver_oracle_report |
access | set_killed |
base fee, fallback fee, capture, relax time, fast half-life, slow half-life, trend gate, step cap, report lifetime, ratio tolerance | Constructor, set_policy_config |
verifier, channel, past window, future window, price bounds | Constructor, set_oracle_config |
pool=0, feed id, emergency admin=0, amm collateral, amm stablecoin, pool precision, admin fee | Constructor |
coin index out of range, not attached, unsafe value for y | get_dy_with_report, get_dy_with_report_age |
The Pyth verifier can revert with its own strings: Insufficient fee provided, input too short, invalid evm magic, invalid signature, and invalid signer.
Earlier deployment
0xAEa95d551154443E73565bE724d59D63d72f8A7d supersedes an earlier YBOraclizedPolicy for the YB-WETH pool, 0x508356890B1AB013b2f184604f403e435C794Cca. The earlier policy has the same configuration and owner. It differs from 0xAEa9…8A7d in these points:
- Its EMAs and target observe the pool's current
last_prices, not the spot stored at the previous callback. - It records the LP of an LT deposit into an empty pool as a gift.
- It has no quote views (
get_dy_with_report*).
The fee formula, corrective test, admission rules, bounds, and kill switch are the same in both. For the addresses of all policy contracts, see Contract addresses.