pub struct RawPoolState {Show 15 fields
pub variant: CurveVariant,
pub balances: Vec<U256>,
pub token_decimals: Vec<u8>,
pub amp: U256,
pub fee: Option<U256>,
pub mid_fee: Option<U256>,
pub out_fee: Option<U256>,
pub fee_gamma: Option<U256>,
pub offpeg_fee_multiplier: Option<U256>,
pub price_scale: Option<Vec<U256>>,
pub d: Option<U256>,
pub gamma: Option<U256>,
pub dynamic_rates: Option<Vec<Option<U256>>>,
pub precisions: Option<Vec<U256>>,
pub eth_variant: Option<bool>,
}Expand description
Raw on-chain pool state, as collected by a transport (RPC, Substreams, etc.).
The consumer fills in the fields relevant to the pool’s CurveVariant,
then calls build_pool to get a crate::evm::protocol::curve::math::Pool ready for swap
computation.
Fields§
§variant: CurveVariantWhich Curve variant this pool is.
balances: Vec<U256>Token balances in native token units (wei). Updates every block.
Length determines coin count (2, 3, or 4+).
§Gotchas
StableSwapNG: balances(i) on-chain returns balanceOf(pool) - admin_balances[i],
excluding uncollected admin fees. An indexer tracking ERC20 Transfer events sees
balanceOf changes, not balances(). The difference is small but causes wei-level
mismatches. Use the balances() getter, not balanceOf.
Rebase tokens (e.g. stETH): balances change without Transfer events — the token
rebases in-place. Requires calling balanceOf(pool) on the rebase token each block.
token_decimals: Vec<u8>Token decimals for each coin (e.g. [18, 6] for ETH/USDC).
Must have the same length as balances.
amp: U256Amplification parameter — already interpolated for the target block. Semi-static: only changes during A ramping (rare admin events, days apart).
This value must match what the on-chain _A() internal function returns
at the target block’s timestamp. It includes the variant’s precision
scaling (A_PRECISION=100 for StableSwap V2+, A_MULTIPLIER=10000 for
CryptoSwap, 1 for V0/V1).
§How to obtain this value
RPC consumer:
Call the pool’s A() view function at the target block.
Returns the already-interpolated value in a single RPC call.
let amp = pool_contract.A().block(block_number).call().await?;Warning: for all A_PRECISION=100 variants (V2, Meta, NG, ALend),
A() returns initial_A / A_PRECISION via integer division, losing
the remainder:
initial_A = 79258
A() = 79258 / 100 = 792 (truncated)
A() * 100 = 79200 ≠ 79258 (lost 58)For exact precision, read initial_A() directly when no ramping
(initial_A == future_A), or use interpolate_a during ramps.
Substreams / storage-based consumer:
Read initial_A, future_A, initial_A_time, future_A_time from
contract storage, then interpolate for the current block’s timestamp:
let amp = curve_adapter::interpolate_a(
initial_a, future_a,
initial_a_time, future_a_time,
block_timestamp,
);For V0/V1 pools that store a single A value (no ramping),
pass that value directly.
Caution with factory deploy events: PlainPoolDeployed and
MetaPoolDeployed events emit A as the user-provided value (e.g. 400),
NOT the raw on-chain value (e.g. 40000). Multiply by A_PRECISION
(100 for V2/NG/Meta/ALend) before passing here.
fee: Option<U256>StableSwap fee. Static (set at pool creation). Required for all StableSwap variants.
mid_fee: Option<U256>CryptoSwap mid fee. Static. Required for all CryptoSwap variants.
out_fee: Option<U256>CryptoSwap out fee. Static. Required for all CryptoSwap variants.
fee_gamma: Option<U256>CryptoSwap fee gamma. Static. Required for all CryptoSwap variants.
offpeg_fee_multiplier: Option<U256>Dynamic fee multiplier. Static. Required for StableSwapNG and StableSwapALend.
price_scale: Option<Vec<U256>>Price scale(s). Updates every block. TwoCrypto: 1 element, TriCrypto: 2 elements. Required for all CryptoSwap variants.
d: Option<U256>Pool invariant D. Updates every block. Required for all CryptoSwap variants.
gamma: Option<U256>Gamma parameter. Semi-static (only changes during admin ramp). Required for TwoCryptoV1, TwoCryptoNG, TriCryptoV1, TriCryptoNG. NOT required for TwoCryptoStable (gamma is ignored).
dynamic_rates: Option<Vec<Option<U256>>>Per-token dynamic rates for StableSwap variants. Depends on token type.
If None, rates are computed from token_decimals as 10^(36 - decimals).
This is correct for v6+ crvUSD factory pools (balances variant) which
use static decimal-based rates and have no stored_rates() getter.
If Some, must have the same length as balances. Each element:
Some(rate)— use this dynamic rate (oracle, ERC4626, etc.)None— compute from decimals as10^(36 - decimals)
§When to provide dynamic rates
StableSwapNG with oracle tokens: Call stored_rates() on the pool
contract each block. Plain tokens return static rates, but ERC4626/oracle
tokens return rates that change per-block.
MetaPools: rates[last] must be base_pool.get_virtual_price().
To find the base pool address: try pool.base_pool() first. MetaPool
Factory proxy pools lack this getter — use factory.get_base_pool(pool)
instead. Do not assume the base pool is 3pool; BTC meta pools use sBTC.
§Gotchas
Fee-on-transfer tokens: actual received amount differs from the transfer parameter. Curve pools generally do not support these tokens, but if encountered, balances will be incorrect.
Tokens with 0 decimals: rate becomes 10^36 — valid but rounding
impact is outsized. Every wei of such a token is worth 10^36 in
normalized space.
precisions: Option<Vec<U256>>On-chain precisions for CryptoSwap variants. Immutable.
If Some, used directly instead of computing from token_decimals.
Read from the pool contract’s precisions() getter.
This is important because some tokens report incorrect decimals()
(e.g. Spectra PT tokens), and the factory computes the correct
precisions at deployment time.
If None, precisions are computed as 10^(18 - decimals).
eth_variant: Option<bool>Selects the deployed Vyper flavour for TwoCryptoV1 only. Immutable.
Legacy 2-coin CryptoSwap V1 pools ship in two flavours whose Newton get_y solver differs
by a single mul2 integer-division grouping (see
[crate::evm::protocol::curve::math::core::twocrypto_v1]). Some(true) selects the ETH
pool grouping (all factory/proxy and WETH-paired legacy pools); Some(false) selects the
non-ETH legacy direct-deploy grouping.
Required for TwoCryptoV1 — build_pool returns BuildError::MissingField if it is
None. Ignored for every other variant. Obtain it via
detect_eth_variant.
Trait Implementations§
Source§impl Clone for RawPoolState
impl Clone for RawPoolState
Source§impl Debug for RawPoolState
impl Debug for RawPoolState
Source§impl Default for RawPoolState
impl Default for RawPoolState
Source§impl<'de> Deserialize<'de> for RawPoolState
impl<'de> Deserialize<'de> for RawPoolState
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for RawPoolState
Source§impl PartialEq for RawPoolState
impl PartialEq for RawPoolState
Source§impl Serialize for RawPoolState
impl Serialize for RawPoolState
impl StructuralPartialEq for RawPoolState
Auto Trait Implementations§
impl Freeze for RawPoolState
impl RefUnwindSafe for RawPoolState
impl Send for RawPoolState
impl Sync for RawPoolState
impl Unpin for RawPoolState
impl UnsafeUnpin for RawPoolState
impl UnwindSafe for RawPoolState
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<'de, T> BorrowedRpcObject<'de> for T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<'de, T> RpcBorrow<'de> for T
impl<T> RpcObject for T
impl<T> RpcRecv for T
impl<T> RpcSend for T
Source§impl<T> Serialize for T
impl<T> Serialize for T
fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>
fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.