@morpho-org/morpho-aave-v3-sdk
TypeScript icon, indicating that this package has built-in type declarations

3.3.0 • Public • Published

Morpho's AaveV3-ETH Optimizer SDK

This repository contains the core typescript features used to build a dapp based on Morpho-AaveV3 smart contracts.

Warning
This package is used by the morpho association to build the morpho-aaveV3 dapp but is still under development and subject to changes.
Use at your own risk. Any feedback is welcome.

Installation

You need to use a node version >= 18.0.0

npm install @morpho-org/morpho-aave-v3-sdk
yarn add @morpho-org/morpho-aave-v3-sdk

Configuration

At the root of your dapp or your script:

/* _app.ts */
import sdk from "@morpho-org/morpho-aave-v3-sdk/configuration";

sdk.setConfiguration(config);

where config is an object with the following optional properties:

property type default description
isProd boolean false Set to true if the dapp is running in production
defaultProvider string process.env.RPC_URL The default provider to use. It fallbacks on the default provider from ethers
defaultMaxIterations { supply: number; borrow: number } { supply: 4, borrow: 4 } Max number of iterations run by the matching engine
gasLimitPercent ethers.BigNumber 11000 (110%) Percentage of the gas estimation used as the gas limit for transactions (with 4 decimals)
percentApproximation ethers.BigNumber 9900 (99%) Scaling applied to transactions' amount to prevent reverting due to block inclusion delay
txSignature string undefined If provided, the signature will be appended to the transaction's data to identify the transaction's origin. It must be in hex format

Usage

The whole sdk is built around the MorphoAaveV3Adapter class. This is the core element of the sdk.

Data structure

Within the adapter, data are stored in different objects:

name public* source interface description
globalData   ✅ yes ⚡️ fetched GlobalData Global data about the chain and the protocol
marketsConfigs   ✅ yes ⚡️ fetched MarketMapping<MarketConfig> Properties of each market that don't (or rarely) change
marketsData   ✅ yes ⚙️ computed MarketMapping<MarketData> Data by market (metrics, apys, ...) that need to be updated frequently
marketsList   ✅ yes ⚡️ fetched string[] List of the markets listed on Morpho-AaveV3
userData   ✅ yes ⚙️ computed UserData User Data that are not specific to a market
userMarketsData   ✅ yes ⚙️ computed MarketMapping<UserMarketData> User Data by market
scaledMarketsData   ❌ no ⚡️ fetched MarketMapping<ScaledMarketData> Raw data by market, before any processing or computation
scaledUserMarketsData   ❌ no ⚡️ fetched MarketMapping<ScaledUserMarketData> Raw user data by market, before any processing or computation
rewardsDistribution   ❌ no ⚡️ fetched MorphoEpochDistribution Morpho rewards distribution of the current epoch

* see the section about data to see how to access public data

Initialization

To create an adapter, you must provide fetchers. These are special entities that are used to fetch data. For each fetcher, you can use one from this fetchers or use your own one (as long as it matches the interface). You have 5 different fetchers:

fetcher fetched data available
MarketFetcher marketsConfigs, marketsList, scaledMarketsData chain, static
UserFetcher scaledUserMarketsData, userData.ethBalance chain, static
GlobalDataFetcher globalData chain, static
RewardsFetcher rewardsDistribution api, static

From chain

If you want to fetch all data from the chain, you can use MorphoAaveV3Adapter.fromChain

const adapter = MorphoAaveV3Adapter.fromChain();
  • you can provide a specific provider from ethers to use:
const adapter = MorphoAaveV3Adapter.fromChain({ provider: myProvider });
await adapter.refreshAll("latest");

by default, the one from the configuration will be used

  • Since some data can't be fetched from chain, you can provide specific fetcher for them:
const adapter = MorphoAaveV3Adapter.fromChain({ extraFetchersConfig });
await adapter.refreshAll("latest");

where extraFetchersConfig has the following interface:

const extraFetchersConfig: {
  rewards?: "api" | RewardsFetcher;
}

By default,

  • marketSupply will be fetched from the morpho-labs subgraph
  • rewards will be fetched from morpho API

From mock

You can also provide static data to the adapter to have a static state in the adapter using MorphoAaveV3Adapter.fromMock

const adapter = MorphoAaveV3Adapter.fromMock(mock);
await adapter.refreshAll("latest");

Where mock can be an AdapterMock. If no mock is provided, this one will be used

Note
You can provide loading delays to the fromMock function for testing purposes to simulate real conditions

Read Data

RxJs

The sdk leverages on RxJS to allow you to build highly reactive apps out of the box. To do so, every public data (see Data structure) are associated with an rxjs Subject:

const adapter = MorphoAaveV3Adapter.fromChain();
await adapter.refreshAll("latest");

adapter.marketsConfigs$.subscribe((marketsConfigs) => ...);
adapter.marketsData$.subscribe((marketsData) => ...);
adapter.userMarketsData$.subscribe((userMarketsData) => ...);
adapter.marketsList$.subscribe((marketsList) => ...);
adapter.userData$.subscribe((userData) => ...);
adapter.globalData$.subscribe((globalData) => ...);

Getters

If you don't use RxJs, you can access these data using getter functions:

const adapter = MorphoAaveV3Adapter.fromChain();
await adapter.refreshAll("latest");

const marketsConfigs = adapter.getMarketsConfigs();
const marketsData = adapter.getMarketsData();
const userMarketsData = adapter.getUserMarketsData();
const marketsList = adapter.getMarketsList();
const userData = adapter.getUserData();
const globalData = adapter.getGlobalData();

Execute a transaction

Notifications

To keep track of what's happening during the transactions' executions, the adapter can be provided with a notifier

adapter.addNotifier(notifier); // Adds `notifier` to the list of the adapter's notifiers.
adapter.removeNotifier(notifier); // Removes `notifier` from the list of adapter's notifiers. It needs to be the same object (reference) as the one that has been added
adapter.resetNotifiers(); // Removes all the notifiers and return them in an array.

A notifier can be any instance/object matching the ITransactionNotifier interface.

The handlers are called according to the following timeline:

transaction flow

Transactions with Morpho-AaveV3 contract

adapter.handleMorphoTransaction(txType, underlyingAddress, amount, options);

with

Param Type Description
txType TransactionType Type of the operation to perfom
underlyingAddress string Address of the underlying market on which to perform the operation
amount ethers.BigNumber Amount of the transaction. Use ethers.constants.MaxUint256 to use the maximum amount
options optional, TransactionOptions Transaction options

Approval

Morpho-AaveV3 leverages the Permit2 Approval feature, but you can still perform classic approvals.

Permit2
adapter.handlePermit2Approval(underlyingAddress, deadline, amount, options);
Param Type Description
underlyingAddress string Address of the underlying token you wanna provide
deadline ethers.BigNumber Deadline after which the approval isn't valid anymore
amount ethers.BigNumber Amount to approve
options optional, ApprovalHandlerOptions Transaction options
Classic
adapter.handleApproval(underlyingAddress, amount, options);
Param Type Description
underlyingAddress string Address of the underlying token you wanna provide
amount ethers.BigNumber Amount to approve
options optional, ApprovalHandlerOptions Transaction options

Claim Morpho rewards

adapter.handleClaimMorpho({ overrides });

Wrap ETH

adapter.handleWrapEth(amount, { overrides });

With amount being of type ethers.BigNumber

Connection

  • Connect a user to the adapter:
adapter.connect(user, signer); // Data will be fetched for `user` and `signer` will be used for transactions
// in read-only

adapter.connect(user); // Data will be fetched for `user` but transactions will be ignored
  • Disconnect connected user:
adapter.disconnect();
  • Get the connection state:
adapter.isConnected();

Refreshing

refreshAll
adapter.refreshAll("latest");

All the data will be refreshed.

Note If the block is undefined, the data will be fetched at the last fetched block. If refreshAll is called for the first time, the data will be fetched at the block "latest"

refreshData
adapter.refreshData();

Fetch a new block from the chain and update all indexes locally without fetching markets data If the block is not a new block, the update will be ignored.

refetchData
adapter.refetchData();

Refetch the data from the chain and recompute computed data.

Note Only globalData, scaledMarketsData, scaledUserMarketsData and rewardsDistribution will be refetched since the others are not likely to change between two blocks

Max capacity

You can use getUserMaxCapacity to get the maximum amount for a given operation on a given market.

const { amount, limiter } = adapter.getUserMaxCapacity(underlyingAddress, txType);

The maximum amount is given in underlying and the limiter is one of the following (see MaxCapacityLimiter)

"LIMITED_BY_WALLET_BALANCE"; // The user can't supply/repay more than his wallet balance
"LIMITED_BY_OPERATION_PAUSED"; // The required operation is paused
"LIMITED_BY_ZERO_PRICE"; // The amount can't be estimated because the fetched price for the given market is zero
"LIMITED_BY_BORROW_CAPACITY"; // The user can't borrow more than his borrow capacity
"LIMITED_BY_POOL_LIQUIDITY"; // The amount is limited by AaveV3 liquidity
"LIMITED_BY_CAP"; // There is a borrow/supply cap on AaveV3 and it limits the operation
"LIMITED_BY_BALANCE"; // The user can't withdraw/repay more than his current balance on Morpho

Simulation

The adapter provides a simulation tool that allows you to simulate the impact of a transaction on its data.

const simulator = adapter.getSimulator(timeout);

with timeout being the minimum delay (in ms) between two refresh. Explicitly set to O to prevent it from refreshing. The default value is 1000 (1s)

Data structure

The simulator has the same data structure as the adapter. See Data Structure for more details.

Note Since the adapter's values are evolving, the simulator will re-run the simulation on the new values when they change.

Simulate

simulator.simulate([
  {
    type,
    amount,
    underlyingAddress
  },
  ...
]);

Reset

Run simulator.reset() reset the operation list.

Note This is equivalent to simulator.simulate([])

Close

When you don't need the simulator anymore, run simulator.close() to free the memory.

Warning Not closing simulators can lead to big performance issues

Package Sidebar

Install

npm i @morpho-org/morpho-aave-v3-sdk

Weekly Downloads

168

Version

3.3.0

License

MIT

Unpacked Size

414 kB

Total Files

161

Last publish

Collaborators

  • rubilmax
  • merlin-egalite
  • mathisgd
  • julien-devatom