Oracle Rate Configuration
What this covers
How to configure oracle-backed pricing floors on EscrowV2 deposits and how that floor interacts with fixed rates and delegated vault rates.
When to use this
Use oracle config when you want deposit pricing to track a market reference instead of relying only on a manually updated fixed floor.
Configure one payment-method and currency pair
import {
getSpreadOracleConfig,
resolveFiatCurrencyBytes32,
resolvePaymentMethodHash,
validateOracleFeedsOnChain,
} from '@zkp2p/sdk';
const paymentMethodHash = resolvePaymentMethodHash('wise', { env: 'production' });
const currencyHash = resolveFiatCurrencyBytes32('USD');
const oracle = getSpreadOracleConfig('USD');
if (!oracle) {
throw new Error('No bundled oracle config for USD');
}
const liveFeeds = await validateOracleFeedsOnChain(client.publicClient);
if (!liveFeeds.has('USD')) {
throw new Error('USD oracle feed is not currently live');
}
await client.setOracleRateConfig({
depositId: 42n,
paymentMethodHash,
currencyHash,
config: {
adapter: oracle.adapter,
adapterConfig: oracle.adapterConfig,
spreadBps: -50,
maxStaleness: oracle.maxStaleness,
},
});
The config fields are:
adapter: the deployed oracle adapter contractadapterConfig: feed-specific bytes for that adapterspreadBps: signed spread in basis points applied to the market pricemaxStaleness: maximum allowed age in seconds before the oracle is ignored
Remove oracle pricing
await client.removeOracleRateConfig({
depositId: 42n,
paymentMethodHash,
currencyHash,
});
After removal, the pair falls back to fixed-rate-only behavior.
Batch-configure several currencies
const usdOracle = getSpreadOracleConfig('USD');
const eurOracle = getSpreadOracleConfig('EUR');
await client.setOracleRateConfigBatch({
depositId: 42n,
paymentMethods: [paymentMethodHash],
currencies: [[
resolveFiatCurrencyBytes32('USD'),
resolveFiatCurrencyBytes32('EUR'),
]],
configs: [[
{
adapter: usdOracle!.adapter,
adapterConfig: usdOracle!.adapterConfig,
spreadBps: -50,
maxStaleness: usdOracle!.maxStaleness,
},
{
adapter: eurOracle!.adapter,
adapterConfig: eurOracle!.adapterConfig,
spreadBps: 0,
maxStaleness: eurOracle!.maxStaleness,
},
]],
});
How the final rate is chosen
EscrowV2 computes:
escrowFloor = max(fixedRate, oracleRate)
effectiveRate = max(escrowFloor, delegatedRate)
Example:
- fixed rate:
1.00 - oracle-derived rate:
1.02 - delegated vault rate:
1.01 - final effective rate:
1.02
If the oracle becomes stale, its contribution becomes 0, so the pair falls back to the fixed floor and then any delegated rate above it.
Key points
- Oracle config is EscrowV2-only
- Negative
spreadBpsis allowed, but the effective multiplier must stay above zero maxStalenessis in seconds, not milliseconds- A stale or invalid oracle does not halt a pair if a non-zero fixed rate still exists
- For background on the contract behavior, see Escrow