Skip to main content

Using Bundlers with TypeScript

The BundlerAction module from @morpho-org/morpho-blue-bundlers provides a comprehensive set of utility functions for encoding various actions that can be bundled together in a single transaction. This tutorial covers the main functions and provides examples of how to use them.

Table of Contents

  1. Installation
  2. Importing BundlerAction
  3. Available Actions
  4. Combining Actions
  5. Best Practices

Installation

First, install the package in your project:

npm install @morpho-org/morpho-blue-bundlers
# or
yarn add @morpho-org/morpho-blue-bundlers

Importing BundlerAction

Import the BundlerAction module in your TypeScript or JavaScript file:

import { BundlerAction } from "@morpho-org/morpho-blue-bundlers";

Available Actions

ERC20 and Native Token Actions

  1. nativeTransfer: Transfer native tokens (e.g., ETH, MATIC)
  2. erc20Transfer: Transfer ERC20 tokens
  3. erc20TransferFrom: Transfer ERC20 tokens from sender to the Bundler

Example:

const recipient = "0x1234...";
const amount = 1000000000000000000n; // 1 ETH
const nativeTransferAction = BundlerAction.nativeTransfer(recipient, amount);

const tokenAddress = "0x5678...";
const erc20TransferAction = BundlerAction.erc20Transfer(
tokenAddress,
recipient,
amount
);

Permit and Permit2 Actions

  1. permit: Permit an ERC20 token
  2. permitDai: Permit DAI token
  3. approve2: Approve using Permit2
  4. transferFrom2: Transfer tokens using Permit2

Example:

import { Signature } from "ethers";

const permitSingle = {
details: {
token: "0x1234...",
amount: 1000000000n,
nonce: 0n,
expiration: 2n ** 48n - 1,
},
spender: bundlerAddress,
sigDeadline: 2n ** 48n - 1,
};

const signature = Signature.from("0xabcdef...");
const approveAction = BundlerAction.approve2(permitSingle, signature, false);

Morpho Actions

  1. morphoSetAuthorizationWithSig: Authorize an account on Morpho
  2. morphoSupply: Supply assets to a Morpho market
  3. morphoSupplyCollateral: Supply collateral to a Morpho market
  4. morphoBorrow: Borrow from a Morpho market
  5. morphoRepay: Repay a Morpho loan
  6. morphoWithdraw: Withdraw supplied assets from Morpho
  7. morphoWithdrawCollateral: Withdraw supplied collateral from Morpho
  8. morphoFlashLoan: Execute a flash loan on Morpho
  9. metaMorphoReallocateTo: Trigger a public reallocation on the PublicAllocator

Example:

const marketParams = {
collateralToken: "0x1234...",
loanToken: "0x5678...",
irm: "0xABCD...",
oracle: "0xEFGH...",
lltv: 860000000000000000n, // 86% LLTV
};

const borrowAmount = 100000000n; // 100 USDC (6 decimals)
const borrower = "0x9876...";
const slippageAmount = 99500000n; // 99.50 USDC, minimum amount of assets to borrow

const borrowAction = BundlerAction.morphoBorrow(
marketParams,
borrowAmount,
0n,
slippageAmount,
borrower
);

ERC4626 Actions

  1. erc4626Mint: Mint shares of an ERC4626 vault
  2. erc4626Deposit: Deposit assets into an ERC4626 vault
  3. erc4626Withdraw: Withdraw assets from an ERC4626 vault
  4. erc4626Redeem: Redeem shares from an ERC4626 vault

Example:

const vaultAddress = "0xABCD...";
const assets = 1000000000000000000n; // 1 token
const minShares = 900000000000000000n; // 0.9 shares
const receiver = "0xEFGH...";

const depositAction = BundlerAction.erc4626Deposit(
vaultAddress,
assets,
minShares,
receiver
);

Universal Rewards Distributor Actions

  1. urdClaim: Claim rewards from the Universal Rewards Distributor

Example:

const distributor = "0x1234...";
const account = "0x5678...";
const reward = "0xABCD...";
const amount = 1000000000000000000n; // 1 token
const proof = ["0xabcd...", "0xdef..."];
const skipRevert = false;

const claimAction = BundlerAction.urdClaim(
distributor,
account,
reward,
amount,
proof,
skipRevert
);

Wrapped Native Token Actions

  1. wrapNative: Wrap native tokens (e.g., ETH to WETH)
  2. unwrapNative: Unwrap native tokens (e.g., WETH to ETH)

Example:

const wrapAmount = 1000000000000000000n; // 1 ETH
const wrapAction = BundlerAction.wrapNative(wrapAmount);

stETH and wstETH Actions

  1. stakeEth: Stake ETH using Lido
  2. wrapStEth: Wrap stETH to wstETH
  3. unwrapStEth: Unwrap wstETH to stETH

Example:

const stakeAmount = 1000000000000000000n; // 1 ETH
const minShares = 950000000000000000n; // 0.95 stETH
const referral = "0x0000...";
const stakeAction = BundlerAction.stakeEth(stakeAmount, minShares, referral);

Protocol-Specific Migration Actions

These actions are available on specific migration bundler instances:

  • AaveV2: repay, withdraw
  • AaveV3: repay, withdraw
  • AaveV3 Optimizer: repay, withdraw, withdrawCollateral, approveManagerWithSig
  • CompoundV2: repay, redeem
  • CompoundV3: repay, withdrawFrom, allowBySig

Example (AaveV3):

const asset = "0x1234...";
const amount = 1000000000000000000n; // 1 token
const rateMode = 2; // Variable rate
const aaveV3RepayAction = BundlerAction.aaveV3Repay(asset, amount, rateMode);

Combining Actions

You can combine multiple actions into a single transaction using the multicall function of your bundler contract:

await bundler
.connect(signer)
.multicall([
BundlerAction.wrapNative(1000000000000000000n),
BundlerAction.erc20Transfer(wethAddress, recipient, 500000000000000000n),
BundlerAction.morphoSupplyCollateral(
marketParams,
500000000000000000n,
borrower,
"0x"
),
BundlerAction.morphoBorrow(
marketParams,
100000000n,
0n,
slippageAmount,
borrower
),
]);

This executes all the encoded actions in a single transaction, saving gas and simplifying complex operations.

Slippage Considerations

Several Bundler actions, particularly those involving Morpho and ERC4626 vaults, include slippage protection parameters. These parameters help protect users from unexpected price movements between transaction submission and execution.
For example, morphoSupply, morphoBorrow, morphoRepay, morphoWithdraw, and other ERC4626 actions all include a slippageAmount or similar parameter. This value represents the minimum (for withdrawals/borrows) or maximum (for deposits/repayments) amount of assets or shares to be transferred, ensuring the transaction reverts if market conditions change unfavorably. When using these actions, carefully calculate and set appropriate slippage tolerances based on the asset's volatility and expected transaction time to balance between execution success and protection against adverse price movements.

For more details about the slippage considerations in the Morpho Interface, refer to the Slippage section of the Morpho Interface documentation.

Best Practices

  1. Always use bigint for numeric values to avoid precision loss.
  2. Double-check addresses and parameters before encoding actions.
  3. Use descriptive variable names to improve code readability.
  4. Leverage TypeScript for better type checking and autocomplete support.
  5. Test your encoded actions thoroughly before deploying to mainnet.
  6. Be aware of the specific bundler instances required for certain actions (e.g., protocol-specific migration actions).
  7. Implement proper error handling and transaction confirmation in your application.
  8. Consider gas optimization by ordering actions efficiently within your multicall.

By mastering these BundlerAction functions, you can create complex, gas-efficient interactions with Morpho and various DeFi protocols in a streamlined manner.