Skip to content

Commit

Permalink
Merge pull request #135 from morpho-org/refactor/market-price
Browse files Browse the repository at this point in the history
Refactor market price
  • Loading branch information
Rubilmax authored Oct 21, 2024
2 parents 93cee0d + 55026f3 commit 962e513
Show file tree
Hide file tree
Showing 24 changed files with 313 additions and 160 deletions.
6 changes: 4 additions & 2 deletions packages/blue-sdk-ethers/src/fetch/Market.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ export async function fetchMarketFromConfig(
params.oracle,
// @ts-ignore incompatible commonjs type
runner,
).price(overrides)
: 0n,
)
.price(overrides)
.catch(() => undefined)
: undefined,
params.irm === adaptiveCurveIrm
? await AdaptiveCurveIrm__factory.connect(
params.irm,
Expand Down
40 changes: 37 additions & 3 deletions packages/blue-sdk-ethers/test/e2e/Market.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { blueAbi } from "./abis.js";
import { test } from "./setup.js";

const { morpho, adaptiveCurveIrm } = addresses[ChainId.EthMainnet];
const { usdc_wstEth, usdc_idle, eth_wstEth } = markets[ChainId.EthMainnet];
const { usdc_wstEth, usdc_idle, eth_wstEth, crvUsd_stkcvx2BTC } =
markets[ChainId.EthMainnet];

describe("augment/Market", () => {
test("should fetch market data", async ({ wallet }) => {
Expand Down Expand Up @@ -38,8 +39,6 @@ describe("augment/Market", () => {
totalBorrowShares: 0n,
lastUpdate: 1711558175n,
fee: 0n,
price: 0n,
rateAtTarget: undefined,
});

const value = await Market.fetch(usdc_idle.id, wallet);
Expand Down Expand Up @@ -105,4 +104,39 @@ describe("augment/Market", () => {

expect(value).toStrictEqual(expectedData);
});

test("should fetch market with incorrect oracle", async ({
client,
wallet,
}) => {
const params = new MarketParams({
...crvUsd_stkcvx2BTC,
oracle: randomAddress(),
});

const timestamp = (await client.timestamp()) + 3n;

await client.setNextBlockTimestamp({ timestamp });
await client.writeContract({
address: morpho,
abi: blueAbi,
functionName: "createMarket",
args: [{ ...params }],
});

const expectedData = new Market({
params,
totalSupplyAssets: 0n,
totalSupplyShares: 0n,
totalBorrowAssets: 0n,
totalBorrowShares: 0n,
lastUpdate: 1711597274n,
fee: 0n,
rateAtTarget: 1268391679n,
});

const value = await Market.fetch(params.id, wallet);

expect(value).toStrictEqual(expectedData);
});
});
6 changes: 5 additions & 1 deletion packages/blue-sdk-viem/contracts/GetMarket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {IAdaptiveCurveIrm} from "./interfaces/IAdaptiveCurveIrm.sol";
struct MarketResponse {
MarketParams marketParams;
Market market;
bool hasPrice;
uint256 price;
uint256 rateAtTarget;
}
Expand All @@ -22,7 +23,10 @@ contract GetMarket {
res.market = morpho.market(id);

if (res.marketParams.oracle != address(0)) {
res.price = IOracle(res.marketParams.oracle).price();
try IOracle(res.marketParams.oracle).price() returns (uint256 price) {
res.hasPrice = true;
res.price = price;
} catch {}
}

if (res.marketParams.irm == address(adaptiveCurveIrm)) {
Expand Down
8 changes: 5 additions & 3 deletions packages/blue-sdk-viem/src/fetch/Market.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export async function fetchMarket(
lastUpdate,
fee,
},
hasPrice,
price,
rateAtTarget,
} = await readContract(client, {
Expand All @@ -55,7 +56,7 @@ export async function fetchMarket(
totalBorrowShares,
lastUpdate,
fee,
price,
price: hasPrice ? price : undefined,
rateAtTarget:
marketParams.irm === adaptiveCurveIrm ? rateAtTarget : undefined,
});
Expand Down Expand Up @@ -108,8 +109,8 @@ export async function fetchMarket(
address: params.oracle,
abi: blueOracleAbi,
functionName: "price",
})
: 0n,
}).catch(() => undefined)
: undefined,
params.irm === adaptiveCurveIrm
? await readContract(client, {
...parameters,
Expand All @@ -120,6 +121,7 @@ export async function fetchMarket(
})
: undefined,
]);

return new Market({
params,
totalSupplyAssets,
Expand Down
3 changes: 2 additions & 1 deletion packages/blue-sdk-viem/src/queries/GetMarket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const abi = [
name: "market",
type: "tuple",
},
{ internalType: "bool", name: "hasPrice", type: "bool" },
{ internalType: "uint256", name: "price", type: "uint256" },
{
internalType: "uint256",
Expand All @@ -96,4 +97,4 @@ export const abi = [
] as const;

export const code =
"0x608080604052346015576104f3908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b34610285576060366003190112610285576004356001600160a01b0381169190829003610285576044356001600160a01b038116929060243590849003610285576080830183811067ffffffffffffffff8211176104275760405260405161008c8161043b565b5f81525f60208201525f60408201525f60608201525f60808201528352602083016040516100b981610457565b5f81525f60208201525f60408201525f60608201525f60808201525f60a0820152815260408401915f835260608501935f8552604051632c3c915760e01b815282600482015260a081602481855afa908115610291575f9161039a575b5060249160c091885260405192838092632e3071cd60e11b82528660048301525afa908115610291575f916102fd575b5082528451604001516001600160a01b03168061029c575b508451606001516001600160a01b0316861461021e575b5060408051945180516001600160a01b039081168752602080830151821681890152828401518216888501526060808401519092168289015260809283015188840152935180516001600160801b0390811660a08a81019190915295820151811660c08a015293810151841660e0890152908101518316610100880152908101518216610120870152909101511661014084015251610160830152516101808201526101a09150f35b6020906024604051809881936301977b5760e01b835260048301525afa948515610291575f95610258575b509382526101a09360a0610175565b94506020853d602011610289575b8161027360209383610473565b810103126102855793519360a0610249565b5f80fd5b3d9150610266565b6040513d5f823e3d90fd5b60206004916040519283809263501ad8ff60e11b82525afa908115610291575f916102cb575b5083525f61015e565b90506020813d6020116102f5575b816102e660209383610473565b8101031261028557515f6102c2565b3d91506102d9565b905060c0813d60c011610392575b8161031860c09383610473565b810103126102855761038760a06040519261033284610457565b61033b816104a9565b8452610349602082016104a9565b602085015261035a604082016104a9565b604085015261036b606082016104a9565b606085015261037c608082016104a9565b6080850152016104a9565b60a08201525f610146565b3d915061030b565b905060a0813d60a01161041f575b816103b560a09383610473565b810103126102855760249160c0916080604051916103d28361043b565b6103db81610495565b83526103e960208201610495565b60208401526103fa60408201610495565b604084015261040b60608201610495565b606084015201516080820152915091610116565b3d91506103a8565b634e487b7160e01b5f52604160045260245ffd5b60a0810190811067ffffffffffffffff82111761042757604052565b60c0810190811067ffffffffffffffff82111761042757604052565b90601f8019910116810190811067ffffffffffffffff82111761042757604052565b51906001600160a01b038216820361028557565b51906001600160801b03821682036102855756fea2646970667358221220efd9cf742cd33c184d8626f47b0484ddcb653e36af9a97223b2dc7be37846d8164736f6c634300081b0033";
"0x608080604052346015576104f8908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b34610285576060366003190112610285576004356001600160a01b0381169190829003610285576044356001600160a01b0381169290602435908490036102855761006f8361042c565b60405161007b8161042c565b5f81525f60208201525f60408201525f60608201525f60808201528352602083016040516100a88161045c565b5f81525f60208201525f60408201525f60608201525f60808201525f60a0820152815260408401905f825260608501925f845260808601945f8652604051632c3c915760e01b815282600482015260a081602481855afa908115610291575f9161039f575b5060249160c091895260405192838092632e3071cd60e11b82528660048301525afa908115610291575f91610302575b5082528551604001516001600160a01b03168061029c575b508551606001516001600160a01b0316871461021e575b5060408051955180516001600160a01b0390811688526020808301518216818a015282840151821689850152606080840151909216828a015260809283015189840152935180516001600160801b0390811660a08b81019190915295820151811660c08b015293810151841660e08a0152908101518316610100890152908101518216610120880152909101511661014085015251151561016084015251610180830152516101a08201526101c09150f35b6020906024604051809981936301977b5760e01b835260048301525afa958615610291575f96610258575b509483526101c09460a061016c565b95506020863d602011610289575b8161027360209383610478565b810103126102855794519460a0610249565b5f80fd5b3d9150610266565b6040513d5f823e3d90fd5b60206004916040519283809263501ad8ff60e11b82525afa5f91816102ce575b5015610155576001845284525f610155565b9091506020813d6020116102fa575b816102ea60209383610478565b810103126102855751905f6102bc565b3d91506102dd565b905060c0813d60c011610397575b8161031d60c09383610478565b810103126102855761038c60a0604051926103378461045c565b610340816104ae565b845261034e602082016104ae565b602085015261035f604082016104ae565b6040850152610370606082016104ae565b6060850152610381608082016104ae565b6080850152016104ae565b60a08201525f61013d565b3d9150610310565b905060a0813d60a011610424575b816103ba60a09383610478565b810103126102855760249160c0916080604051916103d78361042c565b6103e08161049a565b83526103ee6020820161049a565b60208401526103ff6040820161049a565b60408401526104106060820161049a565b60608401520151608082015291509161010d565b3d91506103ad565b60a0810190811067ffffffffffffffff82111761044857604052565b634e487b7160e01b5f52604160045260245ffd5b60c0810190811067ffffffffffffffff82111761044857604052565b90601f8019910116810190811067ffffffffffffffff82111761044857604052565b51906001600160a01b038216820361028557565b51906001600160801b03821682036102855756fea2646970667358221220acbd98f027aaca3ed2f90675c4eece5d7bd1a9fbbbb62956dae5a7491ebc745564736f6c634300081b0033";
37 changes: 34 additions & 3 deletions packages/blue-sdk-viem/test/Market.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { blueAbi } from "../src/index.js";
import { test } from "./setup.js";

const { morpho, adaptiveCurveIrm } = addresses[ChainId.EthMainnet];
const { usdc_wstEth, usdc_idle, eth_wstEth } = markets[ChainId.EthMainnet];
const { usdc_wstEth, usdc_idle, eth_wstEth, crvUsd_stkcvx2BTC } =
markets[ChainId.EthMainnet];

describe("augment/Market", () => {
test("should fetch market data", async ({ client }) => {
Expand Down Expand Up @@ -38,8 +39,6 @@ describe("augment/Market", () => {
totalBorrowShares: 0n,
lastUpdate: 1711558175n,
fee: 0n,
price: 0n,
rateAtTarget: undefined,
});

const value = await Market.fetch(usdc_idle.id, client);
Expand Down Expand Up @@ -104,4 +103,36 @@ describe("augment/Market", () => {

expect(value).toStrictEqual(expectedData);
});

test("should fetch market with incorrect oracle", async ({ client }) => {
const params = new MarketParams({
...crvUsd_stkcvx2BTC,
oracle: randomAddress(),
});

const timestamp = (await client.timestamp()) + 3n;

await client.setNextBlockTimestamp({ timestamp });
await client.writeContract({
address: morpho,
abi: blueAbi,
functionName: "createMarket",
args: [{ ...params }],
});

const expectedData = new Market({
params,
totalSupplyAssets: 0n,
totalSupplyShares: 0n,
totalBorrowAssets: 0n,
totalBorrowShares: 0n,
lastUpdate: 1711597274n,
fee: 0n,
rateAtTarget: 1268391679n,
});

const value = await Market.fetch(params.id, client);

expect(value).toStrictEqual(expectedData);
});
});
6 changes: 6 additions & 0 deletions packages/blue-sdk/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ export namespace BlueErrors {
}
}

export class UnknownOraclePrice extends Error {
constructor(public readonly marketId: MarketId) {
super(`unknown oracle price of market "${marketId}"`);
}
}

export class InsufficientPosition extends Error {
constructor(
public readonly user: Address,
Expand Down
30 changes: 23 additions & 7 deletions packages/blue-sdk/src/market/Market.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ export interface MaxWithdrawCollateralOptions {
export interface MaxPositionCapacities {
supply: CapacityLimit;
withdraw: CapacityLimit;
borrow: CapacityLimit;
borrow: CapacityLimit | undefined;
repay: CapacityLimit;
supplyCollateral: CapacityLimit;
withdrawCollateral: CapacityLimit;
withdrawCollateral: CapacityLimit | undefined;
}

export interface InputMarket {
Expand All @@ -47,7 +47,7 @@ export interface InputMarket {
totalBorrowShares: bigint;
lastUpdate: bigint;
fee: bigint;
price: bigint;
price?: bigint;
rateAtTarget?: bigint;
}

Expand Down Expand Up @@ -88,8 +88,9 @@ export class Market implements InputMarket {

/**
* The price as returned by the market's oracle.
* `undefined` if the oracle is undefined or reverts.
*/
public price: bigint;
public price?: bigint;

/**
* If the market uses the Adaptive Curve IRM, the rate at target utilization.
Expand Down Expand Up @@ -377,6 +378,7 @@ export class Market implements InputMarket {

/**
* Returns the value of a given amount of collateral quoted in loan assets.
* `undefined` iff the market's oracle is undefined or reverts.
* @param collateral The amount of collateral to quote.
*/
public getCollateralValue(collateral: bigint) {
Expand All @@ -385,6 +387,7 @@ export class Market implements InputMarket {

/**
* Returns the maximum debt allowed given a certain amount of collateral.
* `undefined` iff the market's oracle is undefined or reverts.
* To calculate the amount of loan assets that can be borrowed, use `getMaxBorrowableAssets`.
* @param collateral The amount of collateral to consider.
*/
Expand All @@ -399,6 +402,7 @@ export class Market implements InputMarket {

/**
* Returns the maximum amount of loan assets that can be borrowed given a certain borrow position.
* `undefined` iff the market's oracle is undefined or reverts.
* @param position The borrow position to consider.
*/
public getMaxBorrowableAssets(position: {
Expand All @@ -410,6 +414,7 @@ export class Market implements InputMarket {

/**
* Returns the amount of collateral that would be seized in a liquidation given a certain amount of repaid shares.
* `undefined` iff the market's oracle is undefined or reverts.
* @param repaidShares The amount of shares hypothetically repaid.
*/
public getLiquidationSeizedAssets(repaidShares: bigint) {
Expand All @@ -422,6 +427,7 @@ export class Market implements InputMarket {

/**
* Returns the amount of borrow shares that would be repaid in a liquidation given a certain amount of seized collateral.
* `undefined` iff the market's oracle is undefined or reverts.
* @param seizedAssets The amount of collateral hypothetically seized.
*/
public getLiquidationRepaidShares(seizedAssets: bigint) {
Expand All @@ -434,6 +440,7 @@ export class Market implements InputMarket {

/**
* Returns the maximum amount of collateral that is worth being seized in a liquidation given a certain borrow position.
* `undefined` iff the market's oracle is undefined or reverts.
* @param position The borrow position to consider.
*/
public getSeizableCollateral(position: {
Expand All @@ -445,6 +452,7 @@ export class Market implements InputMarket {

/**
* Returns the amount of collateral that can be withdrawn given a certain borrow position.
* `undefined` iff the market's oracle is undefined or reverts.
* @param position The borrow position to consider.
*/
public getWithdrawableCollateral(
Expand All @@ -461,6 +469,7 @@ export class Market implements InputMarket {

/**
* Returns whether a given borrow position is healthy.
* `undefined` iff the market's oracle is undefined or reverts.
* @param position The borrow position to check.
*/
public isHealthy(position: { collateral: bigint; borrowShares: bigint }) {
Expand All @@ -481,6 +490,7 @@ export class Market implements InputMarket {
/**
* Returns the price variation required for the given position to reach its liquidation threshold (scaled by WAD).
* Negative when healthy (the price needs to drop x%), positive when unhealthy (the price needs to soar x%).
* Returns `undefined` iff the market's price is undefined.
* Returns null if the position is not a borrow.
* @param position The borrow position to consider.
*/
Expand Down Expand Up @@ -528,6 +538,7 @@ export class Market implements InputMarket {
/**
* Returns the maximum amount of loan assets that can be borrowed given a certain borrow position
* and the reason for the limit.
* Returns `undefined` iff the market's price is undefined.
* @param position The borrow position to consider.
*/
public getBorrowCapacityLimit(
Expand All @@ -539,10 +550,13 @@ export class Market implements InputMarket {
borrowShares?: bigint;
},
options?: MaxBorrowOptions,
): CapacityLimit {
): CapacityLimit | undefined {
const maxBorrowAssets = this.getMaxBorrowAssets(collateral, options);
if (maxBorrowAssets == null) return;

// handle edge cases when the user is liquidatable (maxBorrow < borrow)
const maxBorrowableAssets = MathLib.zeroFloorSub(
this.getMaxBorrowAssets(collateral, options),
maxBorrowAssets,
this.toBorrowAssets(borrowShares),
);

Expand Down Expand Up @@ -611,6 +625,7 @@ export class Market implements InputMarket {
/**
* Returns the maximum amount of collateral assets that can be withdrawn given a certain borrow position
* and the reason for the limit.
* Returns `undefined` iff the market's price is undefined.
* @param position The borrow position to consider.
*/
public getWithdrawCollateralCapacityLimit(
Expand All @@ -619,11 +634,12 @@ export class Market implements InputMarket {
borrowShares: bigint;
},
options?: MaxWithdrawCollateralOptions,
): CapacityLimit {
): CapacityLimit | undefined {
const withdrawableCollateral = this.getWithdrawableCollateral(
position,
options,
);
if (withdrawableCollateral == null) return;

if (position.collateral > withdrawableCollateral)
return {
Expand Down
Loading

0 comments on commit 962e513

Please sign in to comment.