# CryptoFromPoolVault

> import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# CryptoFromPoolVault

This oracle contract takes the **price oracle from a Curve liquidity pool and applies the redemption of the vault token to it**. This is often used when having **ERC-4626 Vault tokens** with `pricePerShare`, `convertToAsset`, or other similar functions which essentially return the price of one vault token compared to the underlying assets. The first oracle contracts were deployed without considering the [aggregated price of crvUSD](../../crvusd/oracles/price-aggregator.md), but experience has shown that it makes sense to include this value in the calculation. The respective differences are documented in the relevant sections.

These kinds of oracle contracts **need to be deployed manually**, as there is currently no `Factory` to do so.

:::vyper[`CryptoFromPoolVault.vy`]

The source code for the `CryptoFromPoolVault.vy` contract can be found on [GitHub](https://github.com/curvefi/curve-stablecoin/blob/master/contracts/price_oracles/CryptoFromPoolVault.vy). A variant that includes the aggregated crvUSD price, [`CryptoFromPoolVaultWAgg.vy`](https://github.com/curvefi/curve-stablecoin/blob/master/contracts/price_oracles/CryptoFromPoolVaultWAgg.vy), is also available. The contracts are written using [Vyper](https://github.com/vyperlang/vyper) version `0.3.10`.

:::warning[Oracle Suitability]

`CryptoFromPoolVaultWAgg.vy` is only suitable for vaults which cannot be affected by [donation attacks](https://mixbytes.io/blog/overview-of-the-inflation-attack).

:::

:::

:::danger[Oracle Immutability]

The oracle contracts are fully immutable. Once deployed, they cannot change any parameters, stop the price updates, or alter the pools used to calculate the prices. However, because the contract relies on other pools, it's important to keep in mind that changing parameters in the pool, such as the periodicity of the oracle, can influence these oracle contracts. All relevant data required for the oracle to function is passed into the `__init__` function during the deployment of the contract.

<Dropdown title="`__init__`">

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
@external
def __init__(
        pool: Pool,
        N: uint256,
        borrowed_ix: uint256,
        collateral_ix: uint256,
        vault: Vault
    ):
    assert borrowed_ix != collateral_ix
    assert borrowed_ix < N
    assert collateral_ix < N

    POOL = pool
    N_COINS = N
    BORROWED_IX = borrowed_ix
    COLLATERAL_IX = collateral_ix
    VAULT = vault

    no_argument: bool = False
    if N == 2:
        success: bool = False
        res: Bytes[32] = empty(Bytes[32])
        success, res = raw_call(
            pool.address,
            _abi_encode(empty(uint256), method_id=method_id("price_oracle(uint256)")),
            max_outsize=32, is_static_call=True, revert_on_failure=False)
        if not success:
            no_argument = True
    NO_ARGUMENT = no_argument

    self.cached_price_per_share = VAULT.pricePerShare()
    self.cached_timestamp = block.timestamp
```

</TabItem>
</Tabs>

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAgg.vy">

```vyper
@external
def __init__(
        pool: Pool,
        N: uint256,
        borrowed_ix: uint256,
        collateral_ix: uint256,
        vault: Vault,
        agg: StableAggregator
    ):
    assert borrowed_ix != collateral_ix
    assert borrowed_ix < N
    assert collateral_ix < N
    POOL = pool
    N_COINS = N
    BORROWED_IX = borrowed_ix
    COLLATERAL_IX = collateral_ix
    VAULT = vault
    AGG = agg

    no_argument: bool = False
    if N == 2:
        success: bool = False
        res: Bytes[32] = empty(Bytes[32])
        success, res = raw_call(
            pool.address,
            _abi_encode(empty(uint256), method_id=method_id("price_oracle(uint256)")),
            max_outsize=32, is_static_call=True, revert_on_failure=False)
        if not success:
            no_argument = True
    NO_ARGUMENT = no_argument
```

</TabItem>
</Tabs>

</Dropdown>

:::

---

## Oracle Price

The oracle price is calculated by taking the `price_oracle` of a Curve pool and then adjusting it by the redemption rate of a vault, using methods such as `convertToAssets`, `pricePerShare` or really any other equvalent function which returns the rate of the vault token and the underlying asset.

:::example[Example]

Let's take a look at the [sDOLA/crvUSD lending market](https://lend.curve.fi/#/ethereum/markets/one-way-market-17/create), which uses the `CryptoFromPoolVaultWAgg.vy` code. 

The [oracle contract](https://etherscan.io/address/0x002688C4296A2C4d800F271fe6F01741111B09Be) fetches the `price_oracle` of the [DOLA &lt;&gt; crvUSD stableswap-ng pool](https://etherscan.io/address/0x8272E1A3dBef607C04AA6e5BD3a1A134c8ac063B#readContract#F9) and then adjusts this value by the redemption rate obtained from the [`convertToAssets`](https://etherscan.io/address/0xb45ad160634c528Cc3D2926d9807104FA3157305#readContract#F7) method of the [sDOLA vault](https://etherscan.io/address/0xb45ad160634c528Cc3D2926d9807104FA3157305).

:::

Additionally, the `CryptoFromPoolVault.vy` contract has a **built-in mechanism that considers a certain maximum speed of price change within the vault** when calculating the oracle price. This feature is not included in the `CryptoFromPoolVaultWAgg.vy` oracle contract.

<SourceCode>

*The formula to calculate the applied redemption rate is the following:*

$$\min \left( \text{pricePerShare}, \frac{\text{cached\_price\_per\_share} \times (10^{18} + \text{PPS\_MAX\_SPEED} \times (\text{block.timestamp} - \text{cached\_timestamp}))}{10^{18}} \right)$$

In this example, `pricePerShare` is used, but it can really be any equivalent method that returns the redemption rate of the vault token with respect to its underlying token.

`cached_price_per_share` and `cached_timestamp` are internal variables that are updated whenever the `price_w` function is called. The first value is set to the current redemption rate within the vault at the block when the function is called, and the second value to the current timestamp (`block.timestamp`).

<Tabs>
<TabItem value="cryptofrompoolvaul-vy" label="CryptoFromPoolVault.vy">

```vyper
PPS_MAX_SPEED: constant(uint256) = 10**16 / 60  # Max speed of pricePerShare change

cached_price_per_share: public(uint256)
cached_timestamp: public(uint256)

@internal
@view
def _pps() -> uint256:
    return min(VAULT.pricePerShare(), self.cached_price_per_share * (10**18 + PPS_MAX_SPEED * (block.timestamp - self.cached_timestamp)) / 10**18)

@internal
def _pps_w() -> uint256:
    pps: uint256 = min(VAULT.pricePerShare(), self.cached_price_per_share * (10**18 + PPS_MAX_SPEED * (block.timestamp - self.cached_timestamp)) / 10**18)
    self.cached_price_per_share = pps
    self.cached_timestamp = block.timestamp
    return pps
```

</TabItem>
</Tabs>

</SourceCode>

### `price`
::::description[`CryptoFromPoolVault.price() -> uint256: view`]

Getter for the price of the collateral asset denominated against the borrowed token and applying the conversion rate from a vault.

Returns: oracle price (`uint256`).

<SourceCode>

The `CryptoFromPoolVault.vy` oracle contract does not take the aggregated price of crvUSD from the [`PriceAggregator.vy` contract](../../crvusd/oracles/price-aggregator.md) into account. Experience has shown that it makes sense to include this value in the oracle calculations. This is implemented in the `CryptoFromPoolVaultWAgg.vy` oracle contract.

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
interface Pool:
    def price_oracle(i: uint256 = 0) -> uint256: view  # Universal method!

interface StableAggregator:
    def price() -> uint256: view
    def price_w() -> uint256: nonpayable
    def stablecoin() -> address: view

interface Vault:
    def convertToAssets(shares: uint256) -> uint256: view

POOL: public(immutable(Pool))
BORROWED_IX: public(immutable(uint256))
COLLATERAL_IX: public(immutable(uint256))
N_COINS: public(immutable(uint256))
NO_ARGUMENT: public(immutable(bool))
VAULT: public(immutable(Vault))
AGG: public(immutable(StableAggregator))

PPS_MAX_SPEED: constant(uint256) = 10**16 / 60  # Max speed of pricePerShare change

cached_price_per_share: public(uint256)
cached_timestamp: public(uint256)

@external
@view
def price() -> uint256:
    return self._raw_price(self._pps())

@internal
@view
def _raw_price(pps: uint256) -> uint256:
    p_borrowed: uint256 = 10**18
    p_collateral: uint256 = 10**18

    if NO_ARGUMENT:
        p: uint256 = POOL.price_oracle()
        if COLLATERAL_IX > 0:
            p_collateral = p
        else:
            p_borrowed = p

    else:
        if BORROWED_IX > 0:
            p_borrowed = POOL.price_oracle(BORROWED_IX - 1)
        if COLLATERAL_IX > 0:
            p_collateral = POOL.price_oracle(COLLATERAL_IX - 1)

    return p_collateral * pps / p_borrowed

@internal
@view
def _pps() -> uint256:
    return min(VAULT.pricePerShare(), self.cached_price_per_share * (10**18 + PPS_MAX_SPEED * (block.timestamp - self.cached_timestamp)) / 10**18)
```

</TabItem>
</Tabs>

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAgg.vy">

```vyper
interface Pool:
    def price_oracle(i: uint256 = 0) -> uint256: view  # Universal method!

interface StableAggregator:
    def price() -> uint256: view
    def price_w() -> uint256: nonpayable
    def stablecoin() -> address: view

interface Vault:
    def convertToAssets(shares: uint256) -> uint256: view

POOL: public(immutable(Pool))
BORROWED_IX: public(immutable(uint256))
COLLATERAL_IX: public(immutable(uint256))
N_COINS: public(immutable(uint256))
NO_ARGUMENT: public(immutable(bool))
VAULT: public(immutable(Vault))
AGG: public(immutable(StableAggregator))

@external
@view
def price() -> uint256:
    return self._raw_price() * AGG.price() / 10**18

@internal
@view
def _raw_price() -> uint256:
    p_borrowed: uint256 = 10**18
    p_collateral: uint256 = 10**18

    if NO_ARGUMENT:
        p: uint256 = POOL.price_oracle()
        if COLLATERAL_IX > 0:
            p_collateral = p
        else:
            p_borrowed = p

    else:
        if BORROWED_IX > 0:
            p_borrowed = POOL.price_oracle(BORROWED_IX - 1)
        if COLLATERAL_IX > 0:
            p_collateral = POOL.price_oracle(COLLATERAL_IX - 1)

    return p_collateral * VAULT.convertToAssets(10**18) / p_borrowed
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> CryptoFromPoolVault.price()
1046959880335267532
```

</Example>

::::

### `price_w`
::::description[`CryptoFromPoolVault.price_w() -> uint256`]

This function calculates and writes the price while updating `cached_rate` and `cached_timestamp`. This method is called whenever the `_exchange` function is called within the AMM contract of the lending market.

Returns: oracle price (`uint256`).

<SourceCode>

The `CryptoFromPoolVault.vy` oracle contract does not take the aggregated price of crvUSD from the [`PriceAggregator.vy` contract](../../crvusd/oracles/price-aggregator.md) into account. Experience has shown that it makes sense to include this value in the oracle calculations. This is implemented in the `CryptoFromPoolVaultWAgg.vy` oracle contract.

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
interface Pool:
    def price_oracle(i: uint256 = 0) -> uint256: view  # Universal method!

interface StableAggregator:
    def price() -> uint256: view
    def price_w() -> uint256: nonpayable
    def stablecoin() -> address: view

interface Vault:
    def convertToAssets(shares: uint256) -> uint256: view

POOL: public(immutable(Pool))
BORROWED_IX: public(immutable(uint256))
COLLATERAL_IX: public(immutable(uint256))
N_COINS: public(immutable(uint256))
NO_ARGUMENT: public(immutable(bool))
VAULT: public(immutable(Vault))
AGG: public(immutable(StableAggregator))

PPS_MAX_SPEED: constant(uint256) = 10**16 / 60  # Max speed of pricePerShare change

cached_price_per_share: public(uint256)
cached_timestamp: public(uint256)

@external
def price_w() -> uint256:
    return self._raw_price(self._pps_w())

@internal
@view
def _raw_price(pps: uint256) -> uint256:
    p_borrowed: uint256 = 10**18
    p_collateral: uint256 = 10**18

    if NO_ARGUMENT:
        p: uint256 = POOL.price_oracle()
        if COLLATERAL_IX > 0:
            p_collateral = p
        else:
            p_borrowed = p

    else:
        if BORROWED_IX > 0:
            p_borrowed = POOL.price_oracle(BORROWED_IX - 1)
        if COLLATERAL_IX > 0:
            p_collateral = POOL.price_oracle(COLLATERAL_IX - 1)

    return p_collateral * pps / p_borrowed

@internal
def _pps_w() -> uint256:
    pps: uint256 = min(VAULT.pricePerShare(), self.cached_price_per_share * (10**18 + PPS_MAX_SPEED * (block.timestamp - self.cached_timestamp)) / 10**18)
    self.cached_price_per_share = pps
    self.cached_timestamp = block.timestamp
    return pps
```

</TabItem>
</Tabs>

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAgg.vy">

```vyper
interface Pool:
    def price_oracle(i: uint256 = 0) -> uint256: view  # Universal method!

interface StableAggregator:
    def price() -> uint256: view
    def price_w() -> uint256: nonpayable
    def stablecoin() -> address: view

interface Vault:
    def convertToAssets(shares: uint256) -> uint256: view

POOL: public(immutable(Pool))
BORROWED_IX: public(immutable(uint256))
COLLATERAL_IX: public(immutable(uint256))
N_COINS: public(immutable(uint256))
NO_ARGUMENT: public(immutable(bool))
VAULT: public(immutable(Vault))
AGG: public(immutable(StableAggregator))

@external
def price_w() -> uint256:
    return self._raw_price() * AGG.price_w() / 10**18

@internal
@view
def _raw_price() -> uint256:
    p_borrowed: uint256 = 10**18
    p_collateral: uint256 = 10**18

    if NO_ARGUMENT:
        p: uint256 = POOL.price_oracle()
        if COLLATERAL_IX > 0:
            p_collateral = p
        else:
            p_borrowed = p

    else:
        if BORROWED_IX > 0:
            p_borrowed = POOL.price_oracle(BORROWED_IX - 1)
        if COLLATERAL_IX > 0:
            p_collateral = POOL.price_oracle(COLLATERAL_IX - 1)

    return p_collateral * VAULT.convertToAssets(10**18) / p_borrowed
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> CryptoFromPoolVault.price_w()
1046959880335267532
```

</Example>

::::

---

## Contract Info Methods

### `VAULT`
::::description[`CryptoFromPoolVault.VAULT() -> address: view`]

Getter for the vault contract from which the redemption rate (`convertToAssets` or similar functions) is fetched. This value is immutable and set at contract initialization.

Returns: vault contract (`address`).

<SourceCode>

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
VAULT: public(immutable(Vault))
```

</TabItem>
</Tabs>

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAGG.vy">

```vyper
VAULT: public(immutable(Vault))
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> CryptoFromPoolVault.VAULT()
'0xb45ad160634c528Cc3D2926d9807104FA3157305'
```

</Example>

::::

### `POOL`
::::description[`CryptoFromPoolVault.POOL() -> address: view`]

Getter for the liquidity pool used to fetch the `price_oracle`.

Returns: pool contract (`address`).

<SourceCode>

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
POOL: public(immutable(Pool))
```

</TabItem>
</Tabs>

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAgg.vy">

```vyper
POOL: public(immutable(Pool))
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> CryptoFromPoolVault.POOL()
'0x8272E1A3dBef607C04AA6e5BD3a1A134c8ac063B'
```

</Example>

::::

### `BORROWED_IX`
::::description[`CryptoFromPoolVault.BORROWED_IX() -> uint256: view`]

Getter for the coin index of the borrowed token within the pool from which `price_oracle` is fetched. This value is immutable and set at contract initialization.

Returns: coin index (`uint256`).

<SourceCode>

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
BORROWED_IX: public(immutable(uint256))
```

</TabItem>
</Tabs>

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAgg.vy">

```vyper
BORROWED_IX: public(immutable(uint256))
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

`BORROWED_IX` of `1` means the borrowed token in the pool, from which the price oracle value is fetched, is at coin index `1` (`Pool.coins(1)`).

```shell
>>> CryptoFromPoolVaultWAgg.BORROWED_IX()
1
```

</Example>

::::

### `COLLATERAL_IX`
::::description[`CryptoFromPoolVault.COLLATERAL_IX() -> uint256: view`]

Getter for the coin index of the collateral token within the pool from which `price_oracle` is fetched. This value is immutable and set at contract initialization.

Returns: coin index (`uint256`).

<SourceCode>

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
COLLATERAL_IX: public(immutable(uint256))
```

</TabItem>
</Tabs>

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAgg.vy">

```vyper
COLLATERAL_IX: public(immutable(uint256))
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

`COLLATERAL_IX` of `0` means the borrowed token in the pool, from which the price oracle value is fetched, is at coin index `0` (`Pool.coins(0)`).

```shell
>>> CryptoFromPoolVaultWAgg.COLLATERAL_IX()
0
```

</Example>

::::

### `N_COINS`
::::description[`CryptoFromPoolVault.N_COINS() -> uint256: view`]

Getter for the number of coins in `POOL`.

Returns: number of coins (`uint256`).

<SourceCode>

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
N_COINS: public(immutable(uint256))
```

</TabItem>
</Tabs>

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAgg.vy">

```vyper
N_COINS: public(immutable(uint256))
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> CryptoFromPoolVault.N_COINS()
2
```

</Example>

::::

### `NO_ARGUMENT`
::::description[`CryptoFromPoolVault.NO_ARGUMENT() -> bool: view`]

Getter for the `NO_ARGUMENT` storage variable. This is an additional variable to ensure the correct price oracle is fetched from a `POOL`. This value is immutable and set at contract initialization.

Returns: true or false (`bool`).

<SourceCode>

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvault-vy" label="CryptoFromPoolVault.vy">

```vyper
NO_ARGUMENT: public(immutable(bool))
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> CryptoFromPoolVault.NO_ARGUMENT()
'True'
```

</Example>

::::

### `AGG`
::::description[`CryptoFromPoolVaultWAgg.AGG() -> address: view`]

:::info

This `AGG` storage variable is only used within the `CryptoFromPoolVaultWAgg` contracts.

:::

Getter for the crvUSD `PriceAggregator` contract. This value is immutable and set at contract initialization.

Returns: `PriceAggregator` (`address`).

<SourceCode>

The following source code includes all changes up to commit hash [86cae3a](https://github.com/curvefi/curve-stablecoin/tree/86cae3a89f2138122be428b3c060cc75fa1df1b0); any changes made after this commit are not included.

<Tabs>
<TabItem value="cryptofrompoolvaultwagg-vy" label="CryptoFromPoolVaultWAgg.vy">

```vyper
interface StableAggregator:
    def price() -> uint256: view
    def price_w() -> uint256: nonpayable
    def stablecoin() -> address: view

AGG: public(immutable(StableAggregator))
```

</TabItem>
</Tabs>

</SourceCode>

<Example>

```shell
>>> CryptoFromPoolVaultWAgg.AGG()
'0x18672b1b0c623a30089A280Ed9256379fb0E4E62'
```

</Example>

::::
