How to Withdraw

When withdrawing take care to input the correct data. Make sure you are withdrawing to a wallet you have access to on that chain.

Chain abstracted withdrawing has slightly different behaviors compared to a typical withdraw. In the case that there is an issue, whether due to the bridge, there are excess tokens, or invalid parameters were supplied, when bridging the asset from Pool Chain (Arbitrum) to another chain there are a few things to note:

  • The default behavior is that unbridged assets are supplied to the pool on behalf of onBehalfOf. Allows for the user to withdraw the asset on any chain in another transaction.

  • transferExtraAssets is an option to allow for transferring the unbridged assets to onBehalfOf on Arbitrum instead of doing the default behavior above.

Withdrawing Security

To minimize smart contract risk when Replete withdraws assets on behalf of users, Replete utilizes permit signatures. This means all withdrawing functions will have an extra parameter called permitSignature allowing Replete to withdraw a particular asset on behalf of the user via the permit function, up to the allowance provided. This is because the caller for withdraw() will be BridgeReceiver on Arbitrum. Essentially, you are delegating token approval to the BridgeReceiver contract.

The following function is called on the particular rToken:

function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s)

Do not permit more value than you require

It is also fine to not provide a valid permitSignature (set it as empty bytes), as long as BridgeReceiver has enough allowance with the rToken. This can be achieved through calling approve() on the rToken on Arbitrum.

Native Asset

The native asset on Pool Chain is ETH. To withdraw ETH from any chain set asset as BridgeReceiver.ETH_ADDRESS(). You can still withdraw even if ETH is not the native token of the chain, for example you can still withdraw ETH from Avalanche even though the native token is AVAX, as long as there is a bridge route for ETH from Pool Chain to Avalanche.

Any Chain to Pool Chain

You can withdraw any supported asset, assuming you have sufficient collateral, across any chain through Replete.

Interface

 /**
  * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent rTokens owned
  * E.g. User has 100 rUSDC, calls withdraw() and receives 100 USDC, burning the 100 rUSDC
  * @dev The withdrawn assets will be received on Pool Chain
  * @param asset The remote address of the underlying asset to withdraw
  * @param amount The amount to be borrowed in remote asset decimals
  *   - Send the value `type(uint256).max` in order to withdraw the whole rToken balance
  * @param to The remote address that will receive the withdrawn asset on Pool Chain
  * @param permitSignature rToken Permit signature approved for `BridgeReceiver`
  *  encoded as: abi.encode(owner, address(bridgeReceiver), value, deadline, v, r, s)
  * @param lzOptions LayerZero v2 related bridge message options
  */
 function withdraw(
     address asset,
     uint256 amount,
     address to,
     bytes calldata permitSignature,
     bytes calldata lzOptions
 ) external payable;

Example

RepletePool.withdraw{value: bridgeFee}(
    asset: address(DAI),        // DAI address on Pool Chain
    amount: 1337 * 1e18,        // withdraw 1337 DAI
    to: msg.sender,             // receiving address on Pool Chain
    permitSignature: signature, // rDAI permit signature
    lzOptions: lzOptions        // LayerZero v2 composed message execution options
});

Any Chain to Any Chain

Currently, Replete is configured to work with either Stargate or LayerZero to allow bridging support for multiple assets. Withdraw the Replete way.

Interface

Stargate v1

 /**
  * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent rTokens owned
  * E.g. User has 100 rUSDC, calls withdraw() and receives 100 USDC, burning the 100 rUSDC
  * @dev The withdrawn assets will be received on `chainId` chain
  * @param asset The remote address of the underlying asset to withdraw
  * @param amount The amount to be borrowed in remote asset decimals
  *   - Send the value type(uint256).max in order to withdraw the whole rToken balance
  * @param fallbackTo The remote address that will receive the withdrawn asset on Pool Chain.
  *  Used only if `transferExtraAssets` is enabled and there are unbridged assets.
  * @param permitSignature rToken Permit signature approved for `BridgeReceiver`
  *  encoded as: abi.encode(owner, address(bridgeReceiver), value, deadline, v, r, s)
  * @param lzOptions LayerZero v2 related bridge message options
  * @param transferExtraAssets Enable transferring unbridged assets to `onBehalfOf` on Pool Chain
  * @param chainId Destination Stargate v1 chain id
  * @param bridgeParams Stargate v1 related bridge parameters to use when bridging from
  *  Pool Chain --> `chainId` chain
  */
 function withdrawAndBridgeViaStargate(
     address asset,
     uint256 amount,
     address fallbackTo,
     bytes calldata permitSignature,
     bytes calldata lzOptions,
     bool transferExtraAssets,
     uint16 chainId,
     PoolMessageLib.StargateParams calldata bridgeParams
 ) external payable;

Stargate v2

 /**
  * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent rTokens owned
  * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
  * @dev The withdrawn assets will be received on `chainId` chain
  * @param asset The remote address of the underlying asset to withdraw
  * @param amount The amount to be borrowed in remote asset decimals
  *   - Send the value type(uint256).max in order to withdraw the whole rToken balance
  * @param fallbackTo The remote address that will receive the withdrawn asset on Pool Chain.
  *  Used only if `transferExtraAssets` is enabled and there are unbridged assets.
  * @param permitSignature rToken Permit signature approved for `BridgeReceiver`
  *  encoded as: abi.encode(owner, address(bridgeReceiver), value, deadline, v, r, s)
  * @param lzOptions LayerZero v2 related bridge message options
  * @param transferExtraAssets Enable transferring unbridged assets to `onBehalfOf` on Pool Chain
  * @param chainId Destination Stargate v2 chain id
  * @param bridgeParams Stargate v2 related bridge parameters to use when bridging from
  *  Pool Chain --> `chainId` chain
  */
 function withdrawAndBridgeViaStargate(
     address asset,
     uint256 amount,
     address fallbackTo,
     bytes calldata permitSignature,
     bytes calldata lzOptions,
     bool transferExtraAssets,
     uint32 chainId,
     PoolMessageLib.StargateV2Params calldata bridgeParams
 ) external payable;

LayerZero

 /**
  * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent rTokens owned
  * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
  * @dev The withdrawn assets will be received on `chainId` chain
  * @param asset The remote address of the underlying asset to withdraw
  * @param amount The amount to be borrowed in remote asset decimals
  *   - Send the value type(uint256).max in order to withdraw the whole rToken balance
  * @param fallbackTo The remote address that will receive the withdrawn asset on Pool Chain.
  *  Used only if `transferExtraAssets` is enabled and there are unbridged assets.
  * @param permitSignature rToken Permit signature approved for `BridgeReceiver`
  *  encoded as: abi.encode(owner, address(bridgeReceiver), value, deadline, v, r, s)
  * @param lzOptions LayerZero v2 related bridge message options
  * @param transferExtraAssets Enable transferring unbridged assets to `fallbackTo` on Pool Chain
  * @param chainId Destination LayerZero chain id
  * @param bridgeParams LayerZero related bridge parameters to use when bridging from
  *  Pool Chain --> `chainId` chain
  */
 function withdrawAndBridgeViaLayerZero(
     address asset,
     uint256 amount,
     address fallbackTo,
     bytes calldata permitSignature,
     bytes calldata lzOptions,
     bool transferExtraAssets,
     uint32 chainId,
     PoolMessageLib.LayerZeroParams calldata bridgeParams
 ) external payable;

Example

Stargate v1

RepletePool.withdrawAndBridgeViaStargate{value: bridgeFee}(
    asset: address(DAI),        // DAI address on Pool Chain
    amount: 1337 * 1e18,        // withdraw 1337 DAI
    to: msg.sender,             // receiving address on Pool Chain
    permitSignature: signature, // rDAI permit signature
    lzOptions: lzOptions        // LayerZero v2 composed message execution options
    transferExtraAssets: false, // disable the "transferExtraAssets" option
    chainId: destChainId,       // the Stargate v1 destination chain id
    bridgeParams: PoolMessageLib.StargateParams({ // ~~ Stargate v1 related parameters ~~
        to: abi.encodePacked(msg.sender),   // address that will receive DAI on `chainId`
        amount: 1337 * 1e18,                // amount to bridge from Pool Chain to `chainId`
        minAmount: 1337 * 1e18,             // set max slippage amount
        dstGasForCall: 500_000,             // 500k gas for the destination tx
        dstNativeAmount: 0,                 // native gas airdrop in the destination tx
        dstNativeAddr: dstNativeAddr,       // destination address to receive gas airdrop
        srcPoolId: srcPoolId,               // source Stargate v1 pool ID on Pool Chain
        dstPoolId: dstPoolId,               // destination Stargate v1 pool ID from Pool Chain
        refundAddress: payable(msg.sender), // refund address
        payload: payload                    // bytes data to send to `to` on `chainId`
    })
});

Stargate v2

RepletePool.withdrawAndBridgeViaStargate{value: bridgeFee}({
    amount: 1337 * 1e18,        // withdraw 1337 DAI
    to: msg.sender,             // receiving address on Pool Chain
    permitSignature: signature, // rDAI permit signature
    lzOptions: lzOptions        // LayerZero v2 composed message execution options
    transferExtraAssets: false, // disable the "transferExtraAssets" option
    chainId: destChainId,       // the Stargate v2 destination chain id
    bridgeParams: PoolMessageLib.StargateV2Params({ // ~~ Stargate v2 related parameters ~~
        router: stargateDAIPool,                   // Stargate v2 DAI pool address on Pool Chain
        to: bytes32(uint256(uint160(msg.sender))), // address that will receive DAI on `chainId`
        amount: 1337 * 1e18,                       // amount to bridge from Pool Chain to `chainId`
        minAmount: 1337 * 1e18,                    // set max slippage amount
        extraOptions: extraOptions,                // LZv2 composed message execution options
        refundAddress: payable(msg.sender),        // refund address
        payload: payload,                          // bytes data to send to `to` on `chainId`
        oftCmd: oftCmd                             // unused for Stargate taxi transactions
    })
});

LayerZero

RepletePool.withdrawAndBridgeViaLayerZero{value: bridgeFee}({
    amount: 4.20 * 1e18,        // withdraw 4.20 BTC.b
    to: msg.sender,             // receiving address on Pool Chain
    permitSignature: signature, // rBTCb permit signature
    lzOptions: lzOptions        // LayerZero v2 composed message execution options
    transferExtraAssets: false, // disable the "transferExtraAssets" option
    chainId: destChainId,       // the LayerZero destination chain id
    bridgeParams: PoolMessageLib.LayerZeroParams({ // ~~ LayerZero related parameters ~~
        to: bytes32(uint256(uint160(msg.sender))), // address that will receive BTC.b on `chainId`
        amount: 4.20 * 1e18,                       // amount to bridge from Pool Chain to `chainId`
        refundAddress: payable(msg.sender),        // refund address
        payload: payload,                          // bytes data to send to `to` on `chainId`
        // either used for bridging OFTv1 (LZv1), OFTv2 (LZv1) or OFT (LZv2)
        // to provide LZv1 params: abi.encode(address,uint64,bytes)
        //     where the params are: (zroPaymentAddress, dstGasForCall, adapterParams)
        // to provide LZv2 params: abi.encode(uint256,bytes,bytes)
        //     where the params are: (minAmount, extraOptions, oftCmd)
        params: params
    })
})

Last updated