# Reading Asset Balances (/docs/protocols/protocol-fee/guides/read-asset-balances)

Read releasable asset balances across AssetSink, Uniswap v2 LP tokens, and Uniswap v3 pools.

As noted in [Best Practices](/docs/protocols/protocol-fee/guides/best-practices), the `AssetSink's` available asset balance is observed in three places:

* the contract address of the `AssetSink` itself
* the `UniswapV3Pool` contract addresses
* the `UniswapV2Pair` liquidity tokens

For example, the *releasable* USDC balance is `USDC.balanceOf(address(assetSink)) + sum of UniswapV3Pool.protocolFees() + sum of UniswapV2Pair.burn()` for each USDC-paired pool

***

## Uniswap v3 Pool Protocol Fees
Reading available Uniswap v3 Pool protocol fees requires calling `.protocolFees()` on each `UniswapV3Pool` contract

```solidity
import {IERC20} from "forge-std/interfaces/IERC20.sol";
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3Pool.sol";
import {IAssetSink} from "@uniswap/phoenix-fees/src/interfaces/IAssetSink.sol";

contract Example {
    IAssetSink assetSink = IAssetSink(0x0);

    function releaseableBalance(address token, address[] calldata pools)
        external
        view
        returns (uint256)
    {
        return IERC20(token).balanceOf(address(assetSink)) // [!code hl]
            + _getV3ProtocolFeesUnclaimed(token, pools); // [!code hl]
    }

    function _getV3ProtocolFeesUnclaimed(address token, address[] calldata pools)
        internal
        view
        returns (uint256 feesUnclaimed)
    {
        uint128 token0Unclaimed;
        uint128 token1Unclaimed;
        for (uint256 i; i < pools.length; i++) {
            (token0Unclaimed, token1Unclaimed) = IUniswapV3Pool(pools[i]).protocolFees(); // [!code hl]

            // determine if the requested `token` parameter is `pool.token0()` or `pool.token1()`
            feesUnclaimed += pool.token0() == token
                ? token0Unclaimed
                : pool.token1() == token
                ? token1Unclaimed
                : 0;
        }
    }
}
```

> [!NOTE]
> Uniswap v3 does not support native Ether tokens. Protocol fees in ETH are accrued as [Wrapped Ether (WETH)](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2).

Because unclaimed tokens are stored in Uniswap v3 Pool contracts, integrators should use offchain indexing to track
which pools contain the asset of interest. For example, USDC protocol fees are accrued in many different pools:

* USDC / TOKENA 0.05% Fee
* USDC / TOKENA 0.30% Fee
* USDC / TOKENB 0.30% Fee
* USDC / TOKENC 0.30% Fee
* and so on...

To track which pools contain the asset of interest, we recommend to index the `PoolCreated`
[event emitted](https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Factory.sol#L50) by the `UniswapV3Factory` contract

## Uniswap v2 Pool Protocol Fees
Uniswap v2 protocol fees are automatically "pushed" to the `AssetSink` so no additional calls are required to make the assets releasable.
However, Uniswap v2 protocol fees are accrued in the form of liquidity tokens (LP tokens) which are redeemable for underlying assets. Ownership of the LP
token represents the proportional share of the pool's assets.

```text
(LP Token Balance / LP Token Total Supply) * Pool Reserves
```

```solidity
IERC20 lpToken;

uint256 amount0 = (lpToken.balanceOf(address(assetSink)) * IERC20(lpToken.token0()).balanceOf(address(lpToken))) / lpToken.totalSupply();
uint256 amount1 = (lpToken.balanceOf(address(assetSink)) * IERC20(lpToken.token1()).balanceOf(address(lpToken))) / lpToken.totalSupply();
```

> [!NOTE]
> To access the underlying assets, integrators should call [IUniswapV2Pair.burn()](https://github.com/Uniswap/v2-core/blob/ee547b17853e71ed4e0101ccfd52e70d5acded58/contracts/UniswapV2Pair.sol#L134)
> 
>   ```solidity
  (uint256 amount0, uint256 amount1) = IUniswapV2Pair(pool).burn(recipient);
  ```
