@morpho-org/blue-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +98 -0
  2. package/package.json +54 -0
  3. package/src/addresses.ts +261 -0
  4. package/src/chain/chain.constants.ts +235 -0
  5. package/src/chain/chain.test.ts +51 -0
  6. package/src/chain/chain.types.ts +42 -0
  7. package/src/chain/chain.utils.ts +44 -0
  8. package/src/chain/index.ts +2 -0
  9. package/src/constants.ts +18 -0
  10. package/src/errors.ts +75 -0
  11. package/src/ethers/ethers.test.ts +17 -0
  12. package/src/ethers/index.ts +2 -0
  13. package/src/ethers/safeGetAddress.ts +4 -0
  14. package/src/ethers/safeParseUnits.ts +29 -0
  15. package/src/evm.ts +172 -0
  16. package/src/helpers/format/format.test.ts +340 -0
  17. package/src/helpers/format/format.ts +416 -0
  18. package/src/helpers/format/index.ts +1 -0
  19. package/src/helpers/getChecksumedAddress.ts +15 -0
  20. package/src/helpers/index.ts +4 -0
  21. package/src/helpers/isZeroAddressOrUnset.ts +13 -0
  22. package/src/helpers/locale.ts +108 -0
  23. package/src/holding/Holding.ts +109 -0
  24. package/src/holding/index.ts +1 -0
  25. package/src/index.ts +34 -0
  26. package/src/market/Market.ts +479 -0
  27. package/src/market/MarketConfig.ts +108 -0
  28. package/src/market/MarketUtils.test.ts +25 -0
  29. package/src/market/MarketUtils.ts +467 -0
  30. package/src/market/index.ts +3 -0
  31. package/src/maths/AdaptiveCurveIrmLib.ts +143 -0
  32. package/src/maths/MathLib.ts +208 -0
  33. package/src/maths/MathUtils.ts +31 -0
  34. package/src/maths/SharesMath.ts +40 -0
  35. package/src/maths/index.ts +4 -0
  36. package/src/notifications.ts +167 -0
  37. package/src/position/Position.ts +251 -0
  38. package/src/position/index.ts +1 -0
  39. package/src/signatures/index.ts +18 -0
  40. package/src/signatures/manager.ts +50 -0
  41. package/src/signatures/permit.ts +126 -0
  42. package/src/signatures/permit2.ts +120 -0
  43. package/src/signatures/types.ts +18 -0
  44. package/src/signatures/utils.ts +83 -0
  45. package/src/tests/mocks/markets.ts +110 -0
  46. package/src/token/ERC20Metadata.ts +124 -0
  47. package/src/token/Token.ts +83 -0
  48. package/src/token/TokenNamespace.ts +76 -0
  49. package/src/token/WrappedToken.ts +142 -0
  50. package/src/token/index.ts +2 -0
  51. package/src/types.ts +37 -0
  52. package/src/user/User.ts +32 -0
  53. package/src/user/index.ts +2 -0
  54. package/src/user/user.types.ts +23 -0
  55. package/src/vault/Vault.ts +370 -0
  56. package/src/vault/VaultAllocation.ts +58 -0
  57. package/src/vault/VaultConfig.ts +55 -0
  58. package/src/vault/VaultUtils.ts +47 -0
  59. package/src/vault/index.ts +4 -0
@@ -0,0 +1,83 @@
1
+ import { BigNumberish, parseUnits } from "ethers";
2
+
3
+ import { MathLib, RoundingDirection } from "../maths";
4
+ import { Address } from "../types";
5
+
6
+ export interface InputToken {
7
+ address: Address;
8
+ decimals: number;
9
+ symbol: string;
10
+ name?: string;
11
+ }
12
+
13
+ export class Token implements InputToken {
14
+ /**
15
+ * The token's address.
16
+ */
17
+ public readonly address: Address;
18
+
19
+ /**
20
+ * The token's number of decimals.
21
+ */
22
+ public readonly decimals: number;
23
+
24
+ /**
25
+ * The token's symbol.
26
+ */
27
+ public readonly symbol: string;
28
+
29
+ /**
30
+ * The name of the token (defaults to the symbol).
31
+ */
32
+ public readonly name: string;
33
+
34
+ constructor({ address, decimals, symbol, name }: InputToken) {
35
+ this.address = address;
36
+ this.decimals = decimals;
37
+ this.symbol = symbol;
38
+ this.name = name ?? symbol;
39
+ }
40
+ }
41
+
42
+ export class TokenWithPrice extends Token {
43
+ /**
44
+ * Price of the token in USD (scaled by WAD).
45
+ */
46
+ public price?: bigint;
47
+
48
+ constructor(token: InputToken, price?: bigint) {
49
+ super(token);
50
+
51
+ this.price = price;
52
+ }
53
+
54
+ /**
55
+ * Quotes an amount in USD (scaled by WAD) in this token.
56
+ * @param amount The amount of USD to quote.
57
+ */
58
+ fromUsd(amount: BigNumberish, rounding: RoundingDirection = "Down") {
59
+ if (this.price == null) return null;
60
+
61
+ return MathLib.mulDiv(
62
+ amount,
63
+ parseUnits("1", this.decimals),
64
+ this.price,
65
+ rounding
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Quotes an amount of tokens in USD (scaled by WAD).
71
+ * @param amount The amount of tokens to quote.
72
+ */
73
+ toUsd(amount: BigNumberish, rounding: RoundingDirection = "Down") {
74
+ if (this.price == null) return null;
75
+
76
+ return MathLib.mulDiv(
77
+ amount,
78
+ this.price,
79
+ parseUnits("1", this.decimals),
80
+ rounding
81
+ );
82
+ }
83
+ }
@@ -0,0 +1,76 @@
1
+ import { Provider } from "ethers";
2
+ import { WStEth__factory } from "ethers-types";
3
+ import { ViewOverrides } from "ethers-types/dist/common";
4
+
5
+ import {
6
+ NATIVE_ADDRESS,
7
+ getChainAddresses,
8
+ getUnwrappedToken,
9
+ } from "../addresses";
10
+ import { ChainId, ChainUtils } from "../chain";
11
+ import { Address } from "../types";
12
+
13
+ import { ERC20Metadata__factory } from "./ERC20Metadata";
14
+ import { Token as TokenClass } from "./Token";
15
+ import { ConstantWrappedToken, ExchangeRateWrappedToken } from "./WrappedToken";
16
+
17
+ export class Token extends TokenClass {}
18
+
19
+ export { TokenWithPrice } from "./Token";
20
+
21
+ export namespace Token {
22
+ export function native(chainId: ChainId) {
23
+ const currency = ChainUtils.chainMetadata[chainId].nativeCurrency;
24
+
25
+ return new Token({ ...currency, address: NATIVE_ADDRESS });
26
+ }
27
+
28
+ export async function fetch(
29
+ address: Address,
30
+ runner: { provider: Provider },
31
+ {
32
+ chainId,
33
+ overrides = {},
34
+ }: { chainId?: ChainId; overrides?: ViewOverrides } = {}
35
+ ) {
36
+ chainId ??= ChainUtils.parseSupportedChainId(
37
+ (await runner.provider.getNetwork()).chainId
38
+ );
39
+ if (address === NATIVE_ADDRESS) return Token.native(chainId);
40
+
41
+ const erc20 = ERC20Metadata__factory.connect(address, chainId, runner);
42
+
43
+ const [decimals, symbol, name] = await Promise.all([
44
+ erc20.decimals(overrides),
45
+ erc20.symbol(overrides),
46
+ erc20.name(overrides),
47
+ ]);
48
+
49
+ const token = {
50
+ address,
51
+ decimals: parseInt(decimals.toString()),
52
+ symbol,
53
+ name,
54
+ };
55
+
56
+ const { wstEth, stEth } = getChainAddresses(chainId);
57
+
58
+ switch (address) {
59
+ case wstEth: {
60
+ if (stEth) {
61
+ const wstEthToken = WStEth__factory.connect(wstEth!, runner);
62
+ const stEthPerWstEth = await wstEthToken.stEthPerToken(overrides);
63
+ return new ExchangeRateWrappedToken(token, stEth, stEthPerWstEth);
64
+ }
65
+ break;
66
+ }
67
+ }
68
+
69
+ const unwrapToken = getUnwrappedToken(address, chainId);
70
+ if (unwrapToken) {
71
+ return new ConstantWrappedToken(token, unwrapToken, token.decimals); //TODO fetch underlying token's decimals if it can be different form token's decimals
72
+ }
73
+
74
+ return new Token(token);
75
+ }
76
+ }
@@ -0,0 +1,142 @@
1
+ import { formatUnits } from "ethers";
2
+
3
+ import { safeParseUnits } from "../ethers";
4
+ import { MathLib, RoundingDirection, SharesMath } from "../maths";
5
+ import { Address } from "../types";
6
+ import { Vault } from "../vault";
7
+
8
+ import { InputToken, Token } from "./Token";
9
+
10
+ export abstract class WrappedToken extends Token {
11
+ protected abstract _wrap(amount: bigint, rounding: RoundingDirection): bigint;
12
+ protected abstract _unwrap(
13
+ amount: bigint,
14
+ rounding: RoundingDirection
15
+ ): bigint;
16
+
17
+ protected _noSlippage = false;
18
+
19
+ constructor(token: InputToken, readonly underlying: Address) {
20
+ super(token);
21
+ }
22
+
23
+ /** The expected amount when wrapping `unwrappedAmount` */
24
+ toWrappedExactAmountIn(
25
+ unwrappedAmount: bigint,
26
+ slippage = 0n,
27
+ rounding: RoundingDirection = "Down"
28
+ ) {
29
+ const wrappedAmount = this._wrap(unwrappedAmount, rounding);
30
+ if (this._noSlippage) return wrappedAmount;
31
+ return MathLib.wMul(wrappedAmount, MathLib.WAD - slippage, "Down");
32
+ }
33
+
34
+ /** The amount of unwrappedTokens that should be wrapped to receive `wrappedAmount` */
35
+ toWrappedExactAmountOut(
36
+ wrappedAmount: bigint,
37
+ slippage = 0n,
38
+ rounding: RoundingDirection = "Up"
39
+ ) {
40
+ const wAmountTarget = this._noSlippage
41
+ ? wrappedAmount
42
+ : MathLib.wDiv(wrappedAmount, MathLib.WAD - slippage, rounding);
43
+ return this._unwrap(wAmountTarget, rounding);
44
+ }
45
+
46
+ /** The expected amount when unwrapping `wrappedAmount` */
47
+ toUnwrappedExactAmountIn(
48
+ wrappedAmount: bigint,
49
+ slippage = 0n,
50
+ rounding: RoundingDirection = "Down"
51
+ ) {
52
+ const unwrappedAmount = this._unwrap(wrappedAmount, rounding);
53
+ if (this._noSlippage) return unwrappedAmount;
54
+ return MathLib.wMul(unwrappedAmount, MathLib.WAD - slippage, "Up");
55
+ }
56
+
57
+ /** The amount of wrappedTokens that should be unwrapped to receive `unwrappedAmount` */
58
+ toUnwrappedExactAmountOut(
59
+ unwrappedAmount: bigint,
60
+ slippage = 0n,
61
+ rounding: RoundingDirection = "Up"
62
+ ) {
63
+ const unwrappedAmountToTarget = this._noSlippage
64
+ ? unwrappedAmount
65
+ : MathLib.wDiv(unwrappedAmount, MathLib.WAD - slippage, rounding);
66
+ return this._wrap(unwrappedAmountToTarget, rounding);
67
+ }
68
+ }
69
+
70
+ export class ConstantWrappedToken extends WrappedToken {
71
+ protected _noSlippage = true;
72
+
73
+ constructor(
74
+ token: InputToken,
75
+ readonly underlying: Address,
76
+ private readonly _underlyingDecimals = 18
77
+ ) {
78
+ super(token, underlying);
79
+ }
80
+
81
+ protected _wrap(amount: bigint) {
82
+ return safeParseUnits(
83
+ formatUnits(amount, this._underlyingDecimals),
84
+ this.decimals
85
+ );
86
+ }
87
+ protected _unwrap(amount: bigint) {
88
+ return safeParseUnits(
89
+ formatUnits(amount, this.decimals),
90
+ this._underlyingDecimals
91
+ );
92
+ }
93
+ }
94
+
95
+ export class ExchangeRateWrappedToken extends WrappedToken {
96
+ protected _wrap(amount: bigint, rounding: RoundingDirection) {
97
+ return MathLib.wDiv(amount, this.wrappedTokenExchangeRate, rounding);
98
+ }
99
+ protected _unwrap(amount: bigint, rounding: RoundingDirection) {
100
+ return MathLib.wMul(amount, this.wrappedTokenExchangeRate, rounding);
101
+ }
102
+
103
+ constructor(
104
+ token: InputToken,
105
+ readonly underlying: Address,
106
+ public wrappedTokenExchangeRate: bigint
107
+ ) {
108
+ super(token, underlying);
109
+ }
110
+ }
111
+
112
+ export class VaultToken extends WrappedToken {
113
+ protected _wrap(amount: bigint, rounding: RoundingDirection) {
114
+ return SharesMath.toShares(
115
+ amount,
116
+ this.totalAssets,
117
+ this.totalSupply,
118
+ rounding
119
+ );
120
+ }
121
+ protected _unwrap(amount: bigint, rounding: RoundingDirection) {
122
+ return SharesMath.toAssets(
123
+ amount,
124
+ this.totalAssets,
125
+ this.totalSupply,
126
+ rounding
127
+ );
128
+ }
129
+
130
+ public totalAssets: bigint;
131
+ public totalSupply: bigint;
132
+
133
+ constructor(
134
+ token: InputToken,
135
+ readonly underlying: Address,
136
+ { totalAssets, totalSupply }: Pick<Vault, "totalAssets" | "totalSupply">
137
+ ) {
138
+ super(token, underlying);
139
+ this.totalAssets = totalAssets;
140
+ this.totalSupply = totalSupply;
141
+ }
142
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./TokenNamespace";
2
+ export * from "./WrappedToken";
package/src/types.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { isHexString } from "ethers";
2
+
3
+ export type Hex64 = string & { __LENGTH__: 64 };
4
+
5
+ /**
6
+ * The address of a Contract, or an EOA
7
+ */
8
+ export type Address = string;
9
+
10
+ /**
11
+ * The id of a market used on the Blue contract
12
+ */
13
+ export type MarketId = `0x${Hex64}` & { __TYPE__: "marketId" };
14
+
15
+ /**
16
+ * The possible transaction type on the Blue contract
17
+ */
18
+ export enum TransactionType {
19
+ Supply = "Supply",
20
+ SupplyCollateral = "Supply Collateral",
21
+ Withdraw = "Withdraw",
22
+ WithdrawCollateral = "Withdraw Collateral",
23
+ Borrow = "Borrow",
24
+ Repay = "Repay",
25
+ }
26
+
27
+ export type Loadable<T> = T | undefined;
28
+ export type Failable<T> = T | null;
29
+ export type Fetchable<T> = Failable<Loadable<T>>;
30
+
31
+ // TODO: replace with isDefined
32
+ export function isFetched<T>(v: Fetchable<T>): v is T {
33
+ return v !== undefined && v !== null;
34
+ }
35
+
36
+ export const isMarketId = (value: any): value is MarketId =>
37
+ isHexString(value, 32);
@@ -0,0 +1,32 @@
1
+ import { Address } from "../types";
2
+
3
+ export class User {
4
+ /**
5
+ * The user's address.
6
+ */
7
+ public readonly address: Address;
8
+
9
+ /**
10
+ * Whether the bundler is authorized to manage the user's position on Morpho Blue.
11
+ */
12
+ public isBundlerAuthorized: boolean;
13
+
14
+ /**
15
+ * The user's nonce on Morpho Blue.
16
+ */
17
+ public morphoNonce: bigint;
18
+
19
+ constructor({
20
+ address,
21
+ isBundlerAuthorized,
22
+ morphoNonce,
23
+ }: {
24
+ address: Address;
25
+ isBundlerAuthorized: boolean;
26
+ morphoNonce: bigint;
27
+ }) {
28
+ this.address = address;
29
+ this.isBundlerAuthorized = isBundlerAuthorized;
30
+ this.morphoNonce = morphoNonce;
31
+ }
32
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./user.types";
2
+ export * from "./User";
@@ -0,0 +1,23 @@
1
+ import { Token } from "../token";
2
+
3
+ export interface MaxBalanceDecomposition {
4
+ maxBalance: bigint;
5
+ decomposition: { base: BalanceDecompositionFragment } & {
6
+ [T in Exclude<PeripheralTokenType, "base">]?: BalanceDecompositionFragment;
7
+ };
8
+ }
9
+
10
+ export type PeripheralTokenType =
11
+ | "base"
12
+ | "wrapped"
13
+ | "erc4626"
14
+ | "wrapped-erc4626";
15
+ export interface PeripheralToken {
16
+ token: Token;
17
+ type: PeripheralTokenType;
18
+ }
19
+
20
+ export interface BalanceDecompositionFragment extends PeripheralToken {
21
+ underlyingValue: bigint;
22
+ value: bigint;
23
+ }