When borrowing and bridging take care to input the correct data. Make sure you are bridging to a wallet you can control on that chain.
Chain abstracted borrowing has slightly different behaviors compared to a typical borrow. 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.
Borrowing Security
To minimize smart contract risk when Replete borrows assets on behalf of users, Replete utilizes credit delegation signatures. This means all borrowing functions will have an extra parameter called delegationData which contain an EIP712 signature allowing Replete to borrow a particular debt token on behalf of the user via the delegation function, up to the allowance provided. This is because the caller for borrow() will be BridgeReceiver on Arbitrum. Essentially, you are delegating credit to the BridgeReceiver contract.
The following function is called on the particular debt token:
It is also fine to not provide a valid delegationData (set it as empty bytes), as long as BridgeReceiver has enough allowance with the debt token. This can be achieved either through calling approveDelegation(), or calling delegationWithSig() on the debt token on Arbitrum.
The native asset on Pool Chain is ETH. To create a borrow position of ETH from any chain set asset as BridgeReceiver.ETH_ADDRESS(). You can still create a borrow position even if ETH is not the native token of the chain, for example you can still borrow ETH from Avalanche even though the native token is AVAX.
Any Chain to Pool Chain
You can borrow any supported asset, assuming you have sufficient collateral, across any chain through Replete.
Interface
/** * @notice Borrows `amount` of `asset` with a given `interestRateMode`, provided that there * is sufficient collateral provided, or credit has been delegated to the user on the * corresponding debt token (StableDebtToken or VariableDebtToken) * - E.g. User borrows 100 USDC passing as `onBehalfOf` to their debtor, receiving the * 100 USDC in `to` address. * @dev The borrowed assets will be received on Pool Chain * @param asset The remote address of the underlying asset to borrow * @param amount The amount to be borrowed in remote asset decimals * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable * @param referralCode Referral code for integrators -- can safely set to 0 * @param onBehalfOf The remote address of the user who will receive the debt on Pool Chain * @param to The address to send `amount` of `asset` to on Pool Chain * @param lzOptions LayerZero v2 related bridge message options * @param bridgeDelegationData Credit delegation signature approved for `BridgeReceiver` * encoded as: abi.encode(delegator, address(bridgeReceiver), value, deadline, v, r, s) */functionborrow(address asset,uint256 amount,uint8 interestRateMode,uint16 referralCode,address onBehalfOf,address to,bytescalldata lzOptions,bytesmemory bridgeDelegationData) externalpayable;
Example
RepletePool.borrow{value: bridgeFee}({ asset:address(DAI),// DAI address on Pool Chain amount:1337*1e18,// borrow 1337 DAI interestRateMode:2,// variable borrow mode referralCode:0,// referral code; you can safely set to 0 onBehalfOf: msg.sender,// the address containing the collateral for this borrow to: msg.sender,// receiving address on Pool Chain lzOptions: lzOptions,// LayerZero v2 composed message execution options bridgeDelegationData: signature // bridge delegation signature});
Any to Any Chain
Currently, Replete is configured to work with either Stargate or LayerZero to allow bridging support for multiple assets. Borrow the Replete way.
Interface
Stargate v1
/** * @notice Borrows `amount` of `asset` with a given `interestRateMode`, provided that there * is sufficient collateral provided, or credit has been delegated to the user on the * corresponding debt token (StableDebtToken or VariableDebtToken) * - E.g. User borrows 100 USDC passing as `onBehalfOf` to their debtor, receiving the * 100 USDC in `to` address. * @dev The borrowed assets will be received on `chainId` chain * @param asset The remote address of the underlying asset to borrow * @param amount The amount to be borrowed in remote asset decimals * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable * @param referralCode Referral code for integrators -- can safely set to 0 * @param onBehalfOf The remote address of the user who will receive the debt on Pool Chain * @param lzOptions LayerZero v2 related bridge message options * @param bridgeDelegationData Credit delegation signature approved for `BridgeReceiver` * encoded as: abi.encode(delegator, address(bridgeReceiver), value, deadline, v, r, s) * @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 */functionborrowAndBridgeViaStargate(address asset,uint256 amount,uint8 interestRateMode,uint16 referralCode,address onBehalfOf,bytescalldata lzOptions,bytesmemory bridgeDelegationData,bool transferExtraAssets,uint16 chainId,PoolMessageLib.StargateParamscalldata bridgeParams ) externalpayable;
Stargate v2
/** * @notice Borrows `amount` of `asset` with a given `interestRateMode`, provided that there * is sufficient collateral provided, or credit has been delegated to the user on the * corresponding debt token (StableDebtToken or VariableDebtToken) * - E.g. User borrows 100 USDC passing as `onBehalfOf` to their debtor, receiving the * 100 USDC in `to` address. * @dev The borrowed assets will be received on `chainId` chain * @param asset The remote address of the underlying asset to borrow * @param amount The amount to be borrowed in remote asset decimals * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable * @param referralCode Referral code for integrators -- can safely set to 0 * @param onBehalfOf The remote address of the user who will receive the debt on Pool Chain * @param lzOptions LayerZero v2 related bridge message options * @param bridgeDelegationData Credit delegation signature approved for `BridgeReceiver` * encoded as: abi.encode(delegator, address(bridgeReceiver), value, deadline, v, r, s) * @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 */functionborrowAndBridgeViaStargate(address asset,uint256 amount,uint8 interestRateMode,uint16 referralCode,address onBehalfOf,bytescalldata lzOptions,bytesmemory bridgeDelegationData,bool transferExtraAssets,uint32 chainId,PoolMessageLib.StargateV2Paramscalldata bridgeParams ) externalpayable;
LayerZero
/** * @notice Borrows `amount` of `asset` with a given `interestRateMode`, provided that there * is sufficient collateral provided, or credit has been delegated to the user on the * corresponding debt token (StableDebtToken or VariableDebtToken) * - E.g. User borrows 100 USDC passing as `onBehalfOf` to their debtor, receiving the * 100 USDC in `to` address. * @dev The borrowed assets will be received on `chainId` chain * @param asset The remote address of the underlying asset to borrow * @param amount The amount to be borrowed in remote asset decimals * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable * @param referralCode Referral code for integrators -- can safely set to 0 * @param onBehalfOf The remote address of the user who will receive the debt on Pool Chain * @param lzOptions LayerZero v2 related bridge message options * @param bridgeDelegationData Credit delegation signature approved for `BridgeReceiver` * encoded as: abi.encode(delegator, address(bridgeReceiver), value, deadline, v, r, s) * @param transferExtraAssets Enable transferring unbridged assets to `onBehalfOf` on Pool Chain * @param chainId Destination LayerZero chain id * @param bridgeParams LayerZero related bridge parameters to use when bridging from * Pool Chain --> `chainId` chain */functionborrowAndBridgeViaLayerZero(address asset,uint256 amount,uint8 interestRateMode,uint16 referralCode,address onBehalfOf,bytescalldata lzOptions,bytesmemory bridgeDelegationData,bool transferExtraAssets,uint32 chainId,PoolMessageLib.LayerZeroParamscalldata bridgeParams ) externalpayable;
Example
Stargate v1
RepletePool.borrowAndBridgeViaStargate{value: bridgeFee}({ asset:address(DAI),// DAI address on Pool Chain amount:1337*1e18,// borrow 1337 DAI interestRateMode:2,// variable borrow mode referralCode:0,// referral code; you can safely set to 0 onBehalfOf: msg.sender,// the address containing the collateral for this borrow lzOptions: lzOptions,// LayerZero v2 composed message execution options bridgeDelegationData: signature // bridge delegation signature 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.borrowAndBridgeViaStargate{value: bridgeFee}({ asset:address(DAI),// DAI address on Pool Chain amount:1337*1e18,// borrow 1337 DAI interestRateMode:2,// variable borrow mode referralCode:0,// referral code; you can safely set to 0 onBehalfOf: msg.sender,// the address containing the collateral for this borrow lzOptions: lzOptions,// LayerZero v2 composed message execution options bridgeDelegationData: signature // bridge delegation signature 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.borrowAndBridgeViaLayerZero{value: bridgeFee}({ asset:address(BTC_B),// BTC.b address on Pool Chain amount:4.20*1e18,// borrow 4.20 BTC.b interestRateMode:2,// variable borrow mode referralCode:0,// referral code; you can safely set to 0 onBehalfOf: msg.sender,// the address containing the collateral for this borrow lzOptions: lzOptions,// LayerZero v2 composed message execution options bridgeDelegationData: signature // bridge delegation signature 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 })});