Skip to content

Commit 3dfa2d9

Browse files
Morpho Blue Automation (#3849)
* Triggers data in Morpho Blue (#3840) * add automations to morho settings * move triggers data from protocol hooks to generic data hook * update types * Pass additional parameters to triggers API (#3845) * pass protocol and pairId * add additional parameters to create trigger query * format fix * Generalize automation methods and interfaces (#3853) * generalize metadata loading and stop loss * fix stop loss hook * bs hook fixes * auto take profit * trailing stop loss * format fix * typecheck fix * Update front-end automation interfaces to match lambda (#3862) * interface update * tab labels * update triggers mapping * omnikit type fixes * aave type fixes * fix: dynamically generated emptyConfig * features in settngs --------- Co-authored-by: Piotr Konowrocki <piotr@oazoapps.com>
1 parent 8b69d03 commit 3dfa2d9

File tree

86 files changed

+1752
-1376
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+1752
-1376
lines changed

components/context/PreloadAppDataContextProvider.tsx

-8
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { configCacheTime, getLocalAppConfig, saveConfigToLocalStorage } from 'he
44
import { LendingProtocol } from 'lendingProtocols'
55
import type { PropsWithChildren } from 'react'
66
import React, { useContext, useEffect, useState } from 'react'
7-
import { FeaturesEnum } from 'types/config'
87

98
const configFetcher = () =>
109
fetch(`/api/config`, {
@@ -14,13 +13,6 @@ const configFetcher = () =>
1413
},
1514
})
1615

17-
export const emptyConfig = {
18-
features: Object.fromEntries(
19-
Object.values(FeaturesEnum).map((feature: FeaturesEnum) => [feature, false]),
20-
) as Record<FeaturesEnum, boolean | {}>,
21-
parameters: {},
22-
} as ConfigResponseType
23-
2416
export const preloadAppDataContext = React.createContext<PreloadAppDataContext | undefined>(
2517
undefined,
2618
)

features/aave/components/AaveBorrowPositionData.tsx

+12-8
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,14 @@ export function AaveBorrowPositionData({
8282
}: AaveBorrowPositionDataProps) {
8383
const { t } = useTranslation()
8484
const [collateralToken, debtToken] = getCurrentPositionLibCallData(currentPosition)
85-
const stopLossLambdaData = mapStopLossFromLambda(triggersState?.context.currentTriggers.triggers)
86-
const trailingStopLossLambdaData = mapTrailingStopLossFromLambda(
87-
triggersState?.context.currentTriggers.triggers,
88-
)
85+
const stopLossLambdaData = mapStopLossFromLambda({
86+
protocol: lendingProtocol,
87+
triggers: triggersState?.context.currentTriggers.triggers,
88+
})
89+
const trailingStopLossLambdaData = mapTrailingStopLossFromLambda({
90+
protocol: lendingProtocol,
91+
triggers: triggersState?.context.currentTriggers.triggers,
92+
})
8993
const {
9094
triggerData: {
9195
stopLossTriggerData: { stopLossLevel, isStopLossEnabled },
@@ -277,7 +281,7 @@ export function AaveBorrowPositionData({
277281
})
278282

279283
const automationData = useMemo(() => {
280-
if (trailingStopLossLambdaData.trailingStopLossTriggerName) {
284+
if (trailingStopLossLambdaData.trailingStopLossData) {
281285
const collateral = amountFromWei(
282286
currentPosition.collateral.amount || zero,
283287
currentPosition.collateral.precision,
@@ -295,7 +299,7 @@ export function AaveBorrowPositionData({
295299
isTrailingStopLoss: true,
296300
}
297301
}
298-
if (stopLossLambdaData.stopLossTriggerName) {
302+
if (stopLossLambdaData.stopLossData) {
299303
return {
300304
isAutomationAvailable: true,
301305
stopLossLevel: stopLossLambdaData.stopLossLevel?.div(lambdaPercentageDenomination), // still needs to be divided by 100
@@ -317,11 +321,11 @@ export function AaveBorrowPositionData({
317321
isAutomationAvailable,
318322
isAutomationDataLoaded,
319323
isStopLossEnabled,
324+
stopLossLambdaData.stopLossData,
320325
stopLossLambdaData.stopLossLevel,
321-
stopLossLambdaData.stopLossTriggerName,
322326
stopLossLevel,
323327
trailingStopLossLambdaData.dynamicParams?.executionPrice,
324-
trailingStopLossLambdaData.trailingStopLossTriggerName,
328+
trailingStopLossLambdaData.trailingStopLossData,
325329
])
326330

327331
return (

features/aave/components/AaveMultiplyPositionData.tsx

+16-11
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,15 @@ export function AaveMultiplyPositionData({
8888
}: AaveMultiplyPositionDataProps) {
8989
const { t } = useTranslation()
9090
const [collateralToken, debtToken] = getCurrentPositionLibCallData(currentPosition)
91-
const stopLossLambdaData = mapStopLossFromLambda(triggersState?.context.currentTriggers.triggers)
92-
const trailingStopLossLambdaData = mapTrailingStopLossFromLambda(
93-
triggersState?.context.currentTriggers.triggers,
94-
)
91+
92+
const stopLossLambdaData = mapStopLossFromLambda({
93+
protocol: lendingProtocol,
94+
triggers: triggersState?.context.currentTriggers.triggers,
95+
})
96+
const trailingStopLossLambdaData = mapTrailingStopLossFromLambda({
97+
protocol: lendingProtocol,
98+
triggers: triggersState?.context.currentTriggers.triggers,
99+
})
95100
const {
96101
triggerData: {
97102
stopLossTriggerData: { stopLossLevel, isStopLossEnabled },
@@ -269,7 +274,7 @@ export function AaveMultiplyPositionData({
269274
const netValueContentCardAaveData = useAaveCardDataNetValueLending(netValuePnlModalData)
270275

271276
const automationData = useMemo(() => {
272-
if (trailingStopLossLambdaData.trailingStopLossTriggerName) {
277+
if (trailingStopLossLambdaData.trailingStopLossData) {
273278
const collateral = amountFromWei(
274279
currentPosition.collateral.amount || zero,
275280
currentPosition.collateral.precision,
@@ -287,7 +292,7 @@ export function AaveMultiplyPositionData({
287292
isTrailingStopLoss: true,
288293
}
289294
}
290-
if (stopLossLambdaData.stopLossTriggerName) {
295+
if (stopLossLambdaData.stopLossData) {
291296
return {
292297
isAutomationAvailable: true,
293298
stopLossLevel: stopLossLambdaData.stopLossLevel?.div(lambdaPercentageDenomination), // still needs to be divided by 100
@@ -309,15 +314,15 @@ export function AaveMultiplyPositionData({
309314
isAutomationAvailable,
310315
isAutomationDataLoaded,
311316
isStopLossEnabled,
317+
stopLossLambdaData.stopLossData,
312318
stopLossLambdaData.stopLossLevel,
313-
stopLossLambdaData.stopLossTriggerName,
314319
stopLossLevel,
315320
trailingStopLossLambdaData.dynamicParams?.executionPrice,
316-
trailingStopLossLambdaData.trailingStopLossTriggerName,
321+
trailingStopLossLambdaData.trailingStopLossData,
317322
])
318323

319324
const trailingStopLossnotifications = useMemo<DetailsSectionNotificationItem[]>(() => {
320-
if (trailingStopLossLambdaData.trailingStopLossTriggerName) {
325+
if (trailingStopLossLambdaData.trailingStopLossData) {
321326
const { executionPrice, originalExecutionPrice } = trailingStopLossLambdaData.dynamicParams
322327
if (executionPrice.gt(originalExecutionPrice)) {
323328
return [
@@ -344,9 +349,9 @@ export function AaveMultiplyPositionData({
344349
}
345350
return []
346351
}, [
347-
currentPosition.debt.symbol,
352+
priceFormat,
348353
trailingStopLossLambdaData.dynamicParams,
349-
trailingStopLossLambdaData.trailingStopLossTriggerName,
354+
trailingStopLossLambdaData.trailingStopLossData,
350355
])
351356

352357
return (

features/aave/hooks/useOptimizationSidebarDropdown.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ export function useOptimizationSidebarDropdown(
1212
): SidebarSectionHeaderDropdown {
1313
const { t } = useTranslation()
1414

15-
const hasAaveAutoBuyEnabled = state.context.currentTriggers.triggers.aaveBasicBuy !== undefined
16-
const hasSparkAutoBuyEnabled = state.context.currentTriggers.triggers.sparkBasicBuy !== undefined
15+
const hasAaveAutoBuyEnabled = state.context.currentTriggers.triggers.aave3.basicBuy !== undefined
16+
const hasSparkAutoBuyEnabled = state.context.currentTriggers.triggers.spark.basicBuy !== undefined
1717

1818
const hasAavePartialTakeProfitEnabled =
19-
state.context.currentTriggers.triggers.aavePartialTakeProfit !== undefined
19+
state.context.currentTriggers.triggers.aave3.partialTakeProfit !== undefined
2020
const hasSparkPartialTakeProfitEnabled =
21-
state.context.currentTriggers.triggers.sparkPartialTakeProfit !== undefined
21+
state.context.currentTriggers.triggers.spark.partialTakeProfit !== undefined
2222

2323
const currentPanel = useMemo(() => {
2424
return {

features/aave/manage/containers/OptimizationControl.tsx

+12-9
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,11 @@ export function OptimizationControl({
9494
)
9595

9696
// maps the lambda data
97-
const aaveLikePartialTakeProfitLambdaData = mapPartialTakeProfitFromLambda(
98-
state.context.strategyConfig,
99-
triggersState.context.currentTriggers.triggers,
100-
)
97+
const aaveLikePartialTakeProfitLambdaData = mapPartialTakeProfitFromLambda({
98+
protocol: state.context.strategyConfig.protocol,
99+
strategyConfig: state.context.strategyConfig,
100+
triggers: triggersState.context.currentTriggers.triggers,
101+
})
101102
// calculates the partial take profit params + stores user inputs
102103
const aaveLikePartialTakeProfitParams = getAaveLikePartialTakeProfitParams.manage({
103104
state,
@@ -120,11 +121,13 @@ export function OptimizationControl({
120121
const dropdown = useOptimizationSidebarDropdown(triggersState, sendTriggerEvent)
121122

122123
const {
123-
isAaveBasicBuyEnabled,
124-
isSparkBasicBuyEnabled,
125-
isAavePartialTakeProfitEnabled,
126-
isSparkPartialTakeProfitEnabled,
127-
} = triggersState.context.currentTriggers.flags
124+
isBasicBuyEnabled: isAaveBasicBuyEnabled,
125+
isPartialTakeProfitEnabled: isAavePartialTakeProfitEnabled,
126+
} = triggersState.context.currentTriggers.flags.aave3
127+
const {
128+
isBasicBuyEnabled: isSparkBasicBuyEnabled,
129+
isPartialTakeProfitEnabled: isSparkPartialTakeProfitEnabled,
130+
} = triggersState.context.currentTriggers.flags.spark
128131

129132
const autoBuyEnabled = isAaveBasicBuyEnabled || isSparkBasicBuyEnabled
130133
const partialTakeProfitEnabled = isAavePartialTakeProfitEnabled || isSparkPartialTakeProfitEnabled

features/aave/manage/containers/ProtectionControlWrapper.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,14 @@ export function ProtectionControlWrapper({
8989
const [state, send] = useActor(stateMachine)
9090

9191
const [autoSellState, sendAutoSellEvent] = useActor(triggersState.context.autoSellTrigger)
92-
const stopLossLambdaData = mapStopLossFromLambda(triggersState.context.currentTriggers.triggers)
93-
const trailingStopLossLambdaData = mapTrailingStopLossFromLambda(
94-
triggersState.context.currentTriggers.triggers,
95-
)
92+
const stopLossLambdaData = mapStopLossFromLambda({
93+
protocol: state.context.strategyConfig.protocol,
94+
triggers: triggersState.context.currentTriggers.triggers,
95+
})
96+
const trailingStopLossLambdaData = mapTrailingStopLossFromLambda({
97+
protocol: state.context.strategyConfig.protocol,
98+
triggers: triggersState.context.currentTriggers.triggers,
99+
})
96100
const [stopLossToken, setStopLossToken] = useState<'debt' | 'collateral'>(
97101
stopLossLambdaData.stopLossToken ?? 'debt',
98102
)

features/aave/manage/helpers/map-partial-take-profit-from-lambda.ts

+77-73
Original file line numberDiff line numberDiff line change
@@ -11,83 +11,87 @@ import { StrategyType } from 'features/aave/types'
1111
import { formatPercent } from 'helpers/formatters/format'
1212
import type { GetTriggersResponse } from 'helpers/lambda/triggers'
1313
import { nbsp } from 'helpers/nbsp'
14-
import { useMemo } from 'react'
14+
import { LendingProtocol } from 'lendingProtocols'
1515

16-
export const mapPartialTakeProfitFromLambda = (
17-
strategyConfig: IStrategyConfig,
18-
triggers?: GetTriggersResponse['triggers'],
19-
) => {
20-
if (!triggers) {
21-
return {}
22-
}
23-
const isShort = strategyConfig.strategyType === StrategyType.Short
16+
interface MapPartialTakeProfitFromLambdaParams {
17+
poolId?: string
18+
protocol: LendingProtocol
19+
strategyConfig: IStrategyConfig
20+
triggers?: GetTriggersResponse['triggers']
21+
}
2422

25-
const partialTakeProfitTriggersNames = Object.keys(triggers).filter((triggerName) =>
26-
triggerName.includes('PartialTakeProfit'),
27-
)
28-
if (partialTakeProfitTriggersNames.length > 1) {
29-
console.warn(
30-
'Warning: more than one partial take profit trigger found:',
31-
partialTakeProfitTriggersNames,
32-
)
23+
const getTrigger = ({ protocol, triggers, poolId }: MapPartialTakeProfitFromLambdaParams) => {
24+
if (!triggers) return undefined
25+
26+
switch (protocol) {
27+
case LendingProtocol.AaveV3: {
28+
return triggers.aave3.partialTakeProfit
29+
}
30+
case LendingProtocol.MorphoBlue: {
31+
if (`morphoblue-${poolId}` in triggers)
32+
return triggers[`morphoblue-${poolId}`].partialTakeProfit
33+
else return undefined
34+
}
35+
case LendingProtocol.SparkV3: {
36+
return triggers.spark.partialTakeProfit
37+
}
38+
default:
39+
return undefined
3340
}
34-
const partialTakeProfitTriggerName = partialTakeProfitTriggersNames[0] as
35-
| 'sparkPartialTakeProfit'
36-
| 'aavePartialTakeProfit'
37-
const selectedTrigger = triggers[partialTakeProfitTriggerName]
41+
}
3842

39-
const hasStopLoss = hasActiveStopLossFromTriggers({ triggers, protocol: strategyConfig.protocol })
40-
const hasTrailingStopLoss = hasActiveTrailingStopLossFromTriggers({
41-
triggers,
42-
protocol: strategyConfig.protocol,
43-
})
44-
const currentStopLossLevel = useMemo(() => {
45-
return mapStopLossFromLambda(triggers).stopLossLevel
46-
}, [triggers])
47-
const trailingStopLossData = mapTrailingStopLossFromLambda(triggers)
48-
const stopLossTokenLabel = isShort
49-
? `${strategyConfig.tokens.debt}/${strategyConfig.tokens.collateral}`
50-
: `${strategyConfig.tokens.collateral}/${strategyConfig.tokens.debt}`
43+
export const mapPartialTakeProfitFromLambda = ({
44+
poolId,
45+
protocol,
46+
strategyConfig,
47+
triggers,
48+
}: MapPartialTakeProfitFromLambdaParams) => {
49+
if (!triggers) return {}
5150

52-
const triggerLtv = selectedTrigger?.decodedParams.executionLtv
53-
? new BigNumber(Number(selectedTrigger.decodedParams.executionLtv)).div(
54-
lambdaPercentageDenomination,
55-
)
56-
: undefined
57-
const startingTakeProfitPriceLong = selectedTrigger?.decodedParams.executionPrice
58-
? new BigNumber(Number(selectedTrigger.decodedParams.executionPrice)).div(
59-
lambdaPriceDenomination,
60-
)
61-
: undefined
62-
const startingTakeProfitPriceShort = selectedTrigger?.decodedParams.executionPrice
63-
? new BigNumber(lambdaPriceDenomination).div(
64-
new BigNumber(Number(selectedTrigger.decodedParams.executionPrice)),
65-
)
66-
: undefined
67-
const withdrawalLtv =
68-
selectedTrigger?.decodedParams.targetLtv && selectedTrigger?.decodedParams.executionLtv
69-
? new BigNumber(Number(selectedTrigger.decodedParams.targetLtv))
70-
.minus(new BigNumber(Number(selectedTrigger?.decodedParams.executionLtv)))
71-
.div(lambdaPercentageDenomination)
72-
: undefined
73-
const partialTakeProfitToken =
74-
selectedTrigger?.decodedParams.withdrawToDebt === 'true'
75-
? ('debt' as const)
76-
: ('collateral' as const)
51+
const trigger = getTrigger({ poolId, protocol, strategyConfig, triggers })
7752

78-
return {
79-
triggerId: selectedTrigger?.triggerId,
80-
triggerLtv,
81-
startingTakeProfitPrice: isShort ? startingTakeProfitPriceShort : startingTakeProfitPriceLong,
82-
withdrawalLtv,
83-
partialTakeProfitToken,
84-
hasStopLoss: hasStopLoss || hasTrailingStopLoss,
85-
currentStopLossLevel,
86-
currentTrailingDistance: trailingStopLossData.trailingDistance,
87-
stopLossLevelLabel:
88-
hasStopLoss && currentStopLossLevel ? `${formatPercent(currentStopLossLevel)}` : '',
89-
trailingStopLossDistanceLabel: hasTrailingStopLoss
90-
? `${trailingStopLossData.trailingDistance}${nbsp}${stopLossTokenLabel}`
91-
: '',
92-
}
53+
if (trigger) {
54+
const isShort = strategyConfig.strategyType === StrategyType.Short
55+
const hasStopLoss = hasActiveStopLossFromTriggers({ triggers, protocol })
56+
const hasTrailingStopLoss = hasActiveTrailingStopLossFromTriggers({ triggers, protocol })
57+
58+
const currentStopLossLevel = mapStopLossFromLambda({ poolId, protocol, triggers }).stopLossLevel
59+
const trailingStopLossData = mapTrailingStopLossFromLambda({ poolId, protocol, triggers })
60+
61+
const stopLossTokenLabel = isShort
62+
? `${strategyConfig.tokens.debt}/${strategyConfig.tokens.collateral}`
63+
: `${strategyConfig.tokens.collateral}/${strategyConfig.tokens.debt}`
64+
65+
const triggerLtv = new BigNumber(Number(trigger.decodedParams.executionLtv)).div(
66+
lambdaPercentageDenomination,
67+
)
68+
const startingTakeProfitPriceLong = new BigNumber(
69+
Number(trigger.decodedParams.executionPrice),
70+
).div(lambdaPriceDenomination)
71+
const startingTakeProfitPriceShort = new BigNumber(lambdaPriceDenomination).div(
72+
new BigNumber(Number(trigger.decodedParams.executionPrice)),
73+
)
74+
const withdrawalLtv = new BigNumber(Number(trigger.decodedParams.targetLtv))
75+
.minus(new BigNumber(Number(trigger.decodedParams.executionLtv)))
76+
.div(lambdaPercentageDenomination)
77+
78+
const partialTakeProfitToken =
79+
trigger.decodedParams.withdrawToDebt === 'true' ? ('debt' as const) : ('collateral' as const)
80+
81+
return {
82+
triggerId: trigger.triggerId,
83+
triggerLtv,
84+
startingTakeProfitPrice: isShort ? startingTakeProfitPriceShort : startingTakeProfitPriceLong,
85+
withdrawalLtv,
86+
partialTakeProfitToken,
87+
hasStopLoss: hasStopLoss || hasTrailingStopLoss,
88+
currentStopLossLevel,
89+
currentTrailingDistance: trailingStopLossData.trailingDistance,
90+
stopLossLevelLabel:
91+
hasStopLoss && currentStopLossLevel ? `${formatPercent(currentStopLossLevel)}` : '',
92+
trailingStopLossDistanceLabel: hasTrailingStopLoss
93+
? `${trailingStopLossData.trailingDistance}${nbsp}${stopLossTokenLabel}`
94+
: '',
95+
}
96+
} else return {}
9397
}

0 commit comments

Comments
 (0)