DocumentationOpen App
On this pageContract relationships

RWA Pool Deposits and Withdrawals

Some Gearbox RWA markets use DefaultRWAUnderlying 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.

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:

TokenRole
USDCAsset supplied by and returned to the integrator
DefaultRWAUnderlying sharesToken deposited into the Gearbox pool
Gearbox pool sharesERC-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