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:
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 */functionwithdraw(address asset,uint256 amount,address to,bytescalldata permitSignature,bytescalldata lzOptions ) externalpayable;
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 */functionwithdrawAndBridgeViaStargate(address asset,uint256 amount,address fallbackTo,bytescalldata permitSignature,bytescalldata lzOptions,bool transferExtraAssets,uint16 chainId,PoolMessageLib.StargateParamscalldata bridgeParams ) externalpayable;
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 */functionwithdrawAndBridgeViaStargate(address asset,uint256 amount,address fallbackTo,bytescalldata permitSignature,bytescalldata lzOptions,bool transferExtraAssets,uint32 chainId,PoolMessageLib.StargateV2Paramscalldata bridgeParams ) externalpayable;
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 */functionwithdrawAndBridgeViaLayerZero(address asset,uint256 amount,address fallbackTo,bytescalldata permitSignature,bytescalldata lzOptions,bool transferExtraAssets,uint32 chainId,PoolMessageLib.LayerZeroParamscalldata bridgeParams ) externalpayable;
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 })})