How to Borrow

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:

function delegationWithSig(address delegator, address delegatee, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)

Do not delegate more credit than you require

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.

Read more about credit delegation here.

Native Asset

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)
 */
function borrow(
    address asset,
    uint256 amount,
    uint8 interestRateMode,
    uint16 referralCode,
    address onBehalfOf,
    address to,
    bytes calldata lzOptions,
    bytes memory bridgeDelegationData
) external payable;

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
  */
 function borrowAndBridgeViaStargate(
     address asset,
     uint256 amount,
     uint8 interestRateMode,
     uint16 referralCode,
     address onBehalfOf,
     bytes calldata lzOptions,
     bytes memory bridgeDelegationData,
     bool transferExtraAssets,
     uint16 chainId,
     PoolMessageLib.StargateParams calldata bridgeParams
 ) external payable;

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
  */
 function borrowAndBridgeViaStargate(
     address asset,
     uint256 amount,
     uint8 interestRateMode,
     uint16 referralCode,
     address onBehalfOf,
     bytes calldata lzOptions,
     bytes memory bridgeDelegationData,
     bool transferExtraAssets,
     uint32 chainId,
     PoolMessageLib.StargateV2Params calldata bridgeParams
 ) external payable;

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
  */
 function borrowAndBridgeViaLayerZero(
     address asset,
     uint256 amount,
     uint8 interestRateMode,
     uint16 referralCode,
     address onBehalfOf,
     bytes calldata lzOptions,
     bytes memory bridgeDelegationData,
     bool transferExtraAssets,
     uint32 chainId,
     PoolMessageLib.LayerZeroParams calldata bridgeParams
 ) external payable;

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
    })
});

Last updated