3.3.0 • Published 8 months ago

@morpho-org/morpho-aave-v3-sdk v3.3.0

Weekly downloads
-
License
MIT
Repository
github
Last release
8 months ago

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:

propertytypedefaultdescription
isProdbooleanfalseSet to true if the dapp is running in production
defaultProviderstringprocess.env.RPC_URLThe 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
gasLimitPercentethers.BigNumber11000 (110%)Percentage of the gas estimation used as the gas limit for transactions (with 4 decimals)
percentApproximationethers.BigNumber9900 (99%)Scaling applied to transactions' amount to prevent reverting due to block inclusion delay
txSignaturestringundefinedIf 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:

namepublic*sourceinterfacedescription
globalData  ✅ yes⚡️ fetchedGlobalDataGlobal data about the chain and the protocol
marketsConfigs  ✅ yes⚡️ fetchedMarketMapping<MarketConfig>Properties of each market that don't (or rarely) change
marketsData  ✅ yes⚙️ computedMarketMapping<MarketData>Data by market (metrics, apys, ...) that need to be updated frequently
marketsList  ✅ yes⚡️ fetchedstring[]List of the markets listed on Morpho-AaveV3
userData  ✅ yes⚙️ computedUserDataUser Data that are not specific to a market
userMarketsData  ✅ yes⚙️ computedMarketMapping<UserMarketData>User Data by market
scaledMarketsData  ❌ no⚡️ fetchedMarketMapping<ScaledMarketData>Raw data by market, before any processing or computation
scaledUserMarketsData  ❌ no⚡️ fetchedMarketMapping<ScaledUserMarketData>Raw user data by market, before any processing or computation
rewardsDistribution  ❌ no⚡️ fetchedMorphoEpochDistributionMorpho 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:

fetcherfetched dataavailable
MarketFetchermarketsConfigs, marketsList, scaledMarketsDatachain, static
UserFetcherscaledUserMarketsData, userData.ethBalancechain, static
GlobalDataFetcherglobalDatachain, static
RewardsFetcherrewardsDistributionapi, 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

ParamTypeDescription
txTypeTransactionTypeType of the operation to perfom
underlyingAddressstringAddress of the underlying market on which to perform the operation
amountethers.BigNumberAmount of the transaction. Use ethers.constants.MaxUint256 to use the maximum amount
optionsoptional, TransactionOptionsTransaction options

Approval

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

Permit2
adapter.handlePermit2Approval(underlyingAddress, deadline, amount, options);
ParamTypeDescription
underlyingAddressstringAddress of the underlying token you wanna provide
deadlineethers.BigNumberDeadline after which the approval isn't valid anymore
amountethers.BigNumberAmount to approve
optionsoptional, ApprovalHandlerOptionsTransaction options
Classic
adapter.handleApproval(underlyingAddress, amount, options);
ParamTypeDescription
underlyingAddressstringAddress of the underlying token you wanna provide
amountethers.BigNumberAmount to approve
optionsoptional, ApprovalHandlerOptionsTransaction 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

1.5.5

10 months ago

1.6.2

9 months ago

1.6.1

10 months ago

1.6.0

10 months ago

2.2.1

9 months ago

2.1.2

9 months ago

2.2.0

9 months ago

2.1.1

9 months ago

2.0.2

9 months ago

2.2.3

9 months ago

2.2.2

9 months ago

2.1.0

9 months ago

2.0.1

9 months ago

2.0.0

9 months ago

3.3.0

8 months ago

3.2.0

8 months ago

3.1.0

8 months ago

3.0.1

8 months ago

3.0.0

8 months ago

1.5.6

10 months ago

1.5.4

10 months ago

1.5.3

11 months ago

1.5.2

11 months ago

1.5.1

11 months ago

1.5.0

11 months ago

1.4.0

11 months ago

1.3.0

11 months ago

1.2.0

11 months ago

1.1.0

11 months ago

1.0.0

12 months ago