# RWA Pool Deposits and Withdrawals

> Markdown export of the Gearbox Protocol documentation page for agents and retrieval systems.

Canonical page: https://docs.gearbox.finance/developers/rwa-pool-integration
Source file: content/developers/rwa-pool-integration.mdx
Section router: https://docs.gearbox.finance/developers/llms.txt
Section full export: https://docs.gearbox.finance/developers/llms-full.txt

Some Gearbox RWA markets use [`DefaultRWAUnderlying`](https://docs.gearbox.finance/developers/default-rwa-underlying) as the pool underlying. Integrating with these markets requires two ERC-4626 operations:

1. Deposit USDC into `DefaultRWAUnderlying` and receive compliant underlying tokens.
2. Deposit the compliant underlying tokens into the Gearbox pool and receive pool shares.

Withdrawals reverse these operations. For the wrapper's design and compliance behavior, see [Default RWA Underlying](https://docs.gearbox.finance/developers/default-rwa-underlying).

## Contract relationships

All addresses are market-specific. An integration should receive them through configuration and verify their relationship before enabling deposits:

```solidity
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IPoolV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPoolV3.sol";

IERC4626 rwaUnderlying = IERC4626(rwaUnderlyingAddress);
IPoolV3 pool = IPoolV3(poolAddress);

require(rwaUnderlying.asset() == usdcAddress, "RWA underlying asset is not USDC");
require(pool.asset() == rwaUnderlyingAddress, "Unexpected pool underlying");
```

The three tokens have distinct roles:

| Token | Role |
|---|---|
| USDC | Asset supplied by and returned to the integrator |
| `DefaultRWAUnderlying` shares | Token deposited into the Gearbox pool |
| Gearbox pool shares | ERC-4626 shares representing the lender's position in the pool |

## Deposit

Before depositing, preview each ERC-4626 step:

```solidity
uint256 rwaUnderlyingAmount = rwaUnderlying.previewDeposit(usdcAmount);
uint256 expectedPoolShares = pool.previewDeposit(rwaUnderlyingAmount);
```

Then execute the two deposits:

```solidity
IERC20(usdcAddress).approve(rwaUnderlyingAddress, usdcAmount);

uint256 rwaUnderlyingAmount = rwaUnderlying.deposit(
    usdcAmount,
    address(this)
);

IERC20(rwaUnderlyingAddress).approve(poolAddress, rwaUnderlyingAmount);

uint256 poolShares = pool.deposit(
    rwaUnderlyingAmount,
    receiver
);
```

The first deposit returns the same unit amount because the wrapper converts 1:1. The second return value depends on the current pool share price. Integrators should compare `poolShares` with an application-defined minimum calculated from a recent preview.

Approvals are independent: USDC must be approved to `DefaultRWAUnderlying`, and the compliant underlying must be approved to the Gearbox pool.

## Check how much can be withdrawn

Use the pool's ERC-4626 limit function for the position owner:

```solidity
uint256 maxRwaUnderlying = pool.maxWithdraw(owner);
uint256 maxUsdc = rwaUnderlying.previewRedeem(maxRwaUnderlying);
```

Because `DefaultRWAUnderlying` converts 1:1, `maxUsdc` and `maxRwaUnderlying` are normally equal in token units.

`pool.maxWithdraw(owner)` is the appropriate integration limit. It accounts for:

- the owner's pool-share balance;
- the pool's currently available, unborrowed liquidity;
- the configured withdrawal fee; and
- whether the pool is paused.

`pool.availableLiquidity()` exposes the pool-wide amount of compliant underlying currently held by the pool. It does not account for the owner's position and should not be used as the user's withdrawable balance.

For share-denominated exits, use `pool.maxRedeem(owner)`. It returns the maximum pool shares that can currently be redeemed.

## Withdraw an exact USDC amount

Since the compliant underlying converts to USDC 1:1, request the same asset amount from the pool and then redeem it through the wrapper:

```solidity
uint256 sharesToBurn = pool.previewWithdraw(usdcAmount);

uint256 sharesBurned = pool.withdraw(
    usdcAmount,
    address(this),
    owner
);

uint256 usdcReceived = rwaUnderlying.redeem(
    usdcAmount,
    receiver,
    address(this)
);
```

When the calling contract is not `owner`, the owner must first approve the calling contract to spend enough pool shares:

```solidity
IERC20(poolAddress).approve(integratorAddress, sharesToBurn);
```

Integrators should compare `sharesBurned` with an application-defined maximum. A withdrawal can become unavailable between preview and execution if other transactions consume pool liquidity.

## Redeem pool shares

To exit a share-denominated amount, redeem pool shares first and then redeem all compliant underlying received from that operation:

```solidity
uint256 expectedRwaUnderlying = pool.previewRedeem(poolShares);
uint256 expectedUsdc = rwaUnderlying.previewRedeem(expectedRwaUnderlying);

uint256 rwaUnderlyingReceived = pool.redeem(
    poolShares,
    address(this),
    owner
);

uint256 usdcReceived = rwaUnderlying.redeem(
    rwaUnderlyingReceived,
    receiver,
    address(this)
);
```

When the caller differs from `owner`, the same pool-share approval requirement applies.

## Pricing

There are two exchange rates in the complete flow.

### USDC to compliant underlying

This rate is fixed at 1:1 by `DefaultRWAUnderlying`:

```solidity
uint256 rwaUnderlyingOut = rwaUnderlying.convertToShares(usdcAmount);
uint256 usdcOut = rwaUnderlying.convertToAssets(rwaUnderlyingAmount);
```

Use `previewDeposit` and `previewRedeem` when preparing an actual operation, even though the current implementation returns the same amounts.

### Compliant underlying to pool shares

Use the pool's ERC-4626 conversion functions for the current accounting value:

```solidity
uint256 underlyingPerShare = pool.convertToAssets(onePoolShare);
uint256 sharesPerUnderlying = pool.convertToShares(oneUnderlyingToken);
```

Conversions do not include operation-specific fees. Use previews to calculate executable outcomes:

```solidity
uint256 sharesOnDeposit = pool.previewDeposit(rwaUnderlyingAmount);
uint256 underlyingOnRedeem = pool.previewRedeem(poolShares);
uint256 sharesOnWithdraw = pool.previewWithdraw(rwaUnderlyingAmount);
```

For an end-to-end USDC quote, compose both vault previews:

```solidity
// USDC deposit quote
uint256 rwaAmount = rwaUnderlying.previewDeposit(usdcAmount);
uint256 poolSharesOut = pool.previewDeposit(rwaAmount);

// Pool-share redemption quote
uint256 rwaAmountOut = pool.previewRedeem(poolShares);
uint256 usdcOut = rwaUnderlying.previewRedeem(rwaAmountOut);
```

## Operational considerations

- Read token decimals from the contracts and keep all amounts in native token units.
- Treat previews as quotes, not guarantees. Pool interest, fees, and available liquidity can change before execution.
- Use `maxWithdraw(owner)` immediately before an exact-asset withdrawal and `maxRedeem(owner)` before a share-denominated exit.
- Use safe ERC-20 approval handling for tokens that require allowance to be reset before it is changed.

## See also

- [`DefaultRWAUnderlying`](https://docs.gearbox.finance/developers/default-rwa-underlying)
- [Pool Operations](https://docs.gearbox.finance/developers/pool-operations)
- [`IPoolV3`](https://github.com/Gearbox-protocol/core-v3/blob/main/contracts/interfaces/IPoolV3.sol)
- [`IRWAUnderlying`](https://github.com/Gearbox-protocol/periphery-v3/blob/main/contracts/interfaces/base/IRWAUnderlying.sol)
