From 6c289abb337ad9ede0effb282ff4183c445c279b Mon Sep 17 00:00:00 2001 From: Georges KABBOUCHI Date: Sat, 21 Aug 2021 23:13:10 +0300 Subject: [PATCH] Aave v2 Strategies --- components/TokenSelect.vue | 76 ++++ components/TokenSelectOption.vue | 25 ++ components/common/input/ButtonBullet.vue | 10 +- .../context/strategy/SidebarStrategy.vue | 411 ++++++++++++++++++ .../strategy/SidebarStrategySelection.vue | 45 ++ .../strategies/aaveV2/depositAndBorrow.ts | 329 ++++++++++++++ composables/strategies/aaveV2/index.ts | 6 + .../strategies/aaveV2/paybackAndWithdraw.ts | 318 ++++++++++++++ composables/strategies/useStrategy.ts | 62 +++ composables/useBalances.ts | 1 + composables/useMaxAmountPassive.ts | 33 ++ composables/useSidebar.ts | 5 + package.json | 1 + pages/aave-v2.vue | 45 +- pages/mainnet/aave-v2.vue | 216 --------- tailwind.config.js | 1 + yarn.lock | 25 ++ 17 files changed, 1378 insertions(+), 231 deletions(-) create mode 100644 components/TokenSelect.vue create mode 100644 components/TokenSelectOption.vue create mode 100644 components/sidebar/context/strategy/SidebarStrategy.vue create mode 100644 components/sidebar/context/strategy/SidebarStrategySelection.vue create mode 100644 composables/strategies/aaveV2/depositAndBorrow.ts create mode 100644 composables/strategies/aaveV2/index.ts create mode 100644 composables/strategies/aaveV2/paybackAndWithdraw.ts create mode 100644 composables/strategies/useStrategy.ts create mode 100644 composables/useMaxAmountPassive.ts delete mode 100644 pages/mainnet/aave-v2.vue diff --git a/components/TokenSelect.vue b/components/TokenSelect.vue new file mode 100644 index 0000000..755be58 --- /dev/null +++ b/components/TokenSelect.vue @@ -0,0 +1,76 @@ + + + diff --git a/components/TokenSelectOption.vue b/components/TokenSelectOption.vue new file mode 100644 index 0000000..51b5e5d --- /dev/null +++ b/components/TokenSelectOption.vue @@ -0,0 +1,25 @@ + + + diff --git a/components/common/input/ButtonBullet.vue b/components/common/input/ButtonBullet.vue index 307419f..479817a 100644 --- a/components/common/input/ButtonBullet.vue +++ b/components/common/input/ButtonBullet.vue @@ -10,12 +10,10 @@ 'border-grey-pure': !checked }" > - -
-
+
{{ label }}
diff --git a/components/sidebar/context/strategy/SidebarStrategy.vue b/components/sidebar/context/strategy/SidebarStrategy.vue new file mode 100644 index 0000000..ae3f7ca --- /dev/null +++ b/components/sidebar/context/strategy/SidebarStrategy.vue @@ -0,0 +1,411 @@ + + + diff --git a/components/sidebar/context/strategy/SidebarStrategySelection.vue b/components/sidebar/context/strategy/SidebarStrategySelection.vue new file mode 100644 index 0000000..1f017a4 --- /dev/null +++ b/components/sidebar/context/strategy/SidebarStrategySelection.vue @@ -0,0 +1,45 @@ + + + diff --git a/composables/strategies/aaveV2/depositAndBorrow.ts b/composables/strategies/aaveV2/depositAndBorrow.ts new file mode 100644 index 0000000..89a6195 --- /dev/null +++ b/composables/strategies/aaveV2/depositAndBorrow.ts @@ -0,0 +1,329 @@ +import { computed, ref, watch } from "@nuxtjs/composition-api"; +import { useAaveV2Position } from "~/composables/protocols/useAaveV2Position"; +import { useBalances } from "~/composables/useBalances"; +import { useBigNumber } from "~/composables/useBigNumber"; +import { useDSA } from "~/composables/useDSA"; +import { useFormatting } from "~/composables/useFormatting"; +import { useMaxAmountPassive } from "~/composables/useMaxAmountPassive"; +import { useParsing } from "~/composables/useParsing"; +import { useToken } from "~/composables/useToken"; +import { useValidators } from "~/composables/useValidators"; +import { useWeb3 } from "~/composables/useWeb3"; + +export const meta = { + id: "depositAndBorrow", + title: "Deposit & Borrow", + card: { + name: "Deposit & Borrow", + description: "Deposit collateral & borrow asset in a single txn.", + badge: null + }, + protocols: ["aaveV2"] +}; + +export function use() { + const { parseSafeFloat } = useParsing(); + const { account } = useWeb3(); + const { dsa } = useDSA(); + const { + formatUsdMax, + formatUsd, + formatNumber, + formatDecimal + } = useFormatting(); + const { valInt, getTokenByKey } = useToken(); + + const { + validateAmount, + validateLiquidation, + validateIsLoggedIn, + validateLiquidity + } = useValidators(); + const { isZero, plus, div, max } = useBigNumber(); + + const description = computed(() => { + return ` +

This strategy executes:

+ + + `; + }); + + const components = computed(() => [ + { + type: "html", + minHeight: "96px", + html: description.value + }, + { + type: "rate-selection-aave", + onChange: type => (rateType.value = type), + value: rateType.value, + items: annualPercentageRateTypes.value, + borrowStableRate: borrowStableRate.value, + stableBorrowEnabled: stableBorrowEnabled.value + }, + { + type: "input-amount", + placeholder: `${collateralToken.value.symbol} to Deposit`, + value: collateralAmount.value, + tokenKey: collateralToken.value.key, + onInput: value => (collateralAmount.value = value), + errorKey: "collateralAmount" + }, + { + type: "input-amount", + placeholder: `${debtToken.value.symbol} to Borrow`, + value: debtAmount.value, + tokenKey: debtToken.value.key, + onInput: value => (debtAmount.value = value), + errorKey: "debtAmount" + }, + { + type: "heading", + text: "Projected Debt Position" + }, + { + type: "value-with-token-select", + label: "Collateral", + value: formatNumber(collateralBalance.value), + valueBadge: collateralValueBadge.value, + tokens: collateralTokens.value, + tokenKey: collateralToken.value.key, + onChange: tokenKey => (collateralToken.value = getTokenByKey(tokenKey)) + }, + { + type: "value-with-token-select", + label: "Debt", + value: formatNumber(debtBalance.value), + valueBadge: debtValueBadge.value, + tokens: debtTokens.value, + tokenKey: debtToken.value.key, + onChange: tokenKey => (debtToken.value = getTokenByKey(tokenKey)) + }, + { + type: "status", + liquidation: liquidation.value, + status: status.value + }, + { + type: "value", + label: "LIQUIDATION PRICE (IN ETH)", + value: `${formatUsdMax( + liquidationPrice.value, + liquidationMaxPrice.value + )} / ${formatUsd(liquidationMaxPrice.value)}` + }, + { + type: "button-submit", + label: "Deposit & Borrow", + onSubmit: async () => { + const collateralTokenAddr = collateralToken.value.address; + const debtTokenAddr = debtToken.value.address; + + const collateralAmount = isCollateralMaxAmount.value + ? dsa.value.maxValue + : valInt( + collateralAmountParsed.value, + collateralToken.value.decimals + ); + + const debtAmount = valInt( + debtAmountParsed.value, + debtToken.value.decimals + ); + + const spells = dsa.value.Spell(); + + const rateMode = rateType.value?.rateMode; + + spells.add({ + connector: "aave_v2", + method: "deposit", + args: [collateralTokenAddr, collateralAmount, 0, 0] + }); + + spells.add({ + connector: "aave_v2", + method: "borrow", + args: [debtTokenAddr, debtAmount, rateMode, 0, 0] + }); + + await dsa.value.cast({ + spells, + from: account.value + }); + } + } + ]); + + const { position } = useAaveV2Position(); + const { getBalanceByAddress } = useBalances(); + + const collateralAmount = ref(""); + const debtAmount = ref(""); + const collateralAmountParsed = computed(() => + parseSafeFloat(collateralAmount.value) + ); + const debtAmountParsed = computed(() => parseSafeFloat(debtAmount.value)); + const rateType = ref(null); + + const collateralTokens = computed(() => + position.value.data.map(token => token.key) + ); + + const collateralToken = ref(null); + watch( + collateralTokens, + tokens => { + if (tokens.includes(collateralToken.value)) return; + + collateralToken.value = getTokenByKey(collateralTokens.value[0] || "eth"); + }, + { immediate: true } + ); + const borrowEnabled = position => position.borrowEnabled; + const debtTokens = computed(() => + position.value.data.filter(borrowEnabled).map(token => token.key) + ); + + const debtToken = ref(null); + watch( + debtTokens, + tokens => { + if (tokens.includes(debtToken.value)) return; + + debtToken.value = getTokenByKey(debtTokens.value[1] || "dai"); + }, + { immediate: true } + ); + + const collateralBalance = computed( + () => + position.value.data.find( + position => position.key === collateralToken.value.key + )?.supply || "0" + ); + + const debtPosition = computed(() => + position.value.data.find(position => position.key === debtToken.value.key) + ); + const debtBalance = computed(() => { + if (rateType.value?.value === "stable") { + return debtPosition.value?.borrowStable || "0"; + } + return debtPosition.value?.borrow || "0"; + }); + const stableBorrowEnabled = computed( + () => + debtPosition.value?.stableBorrowEnabled && + isZero(debtPosition.value?.supply) + ); + const borrowStableRate = computed( + () => debtPosition.value?.borrowStableRate || "0" + ); + const availableLiquidity = computed( + () => debtPosition.value?.availableLiquidity || "0" + ); + + const collateralDsaBalance = computed(() => + getBalanceByAddress(collateralToken.value.address) + ); + + const collateralValueBadge = computed(() => { + if (isZero(collateralAmountParsed.value)) return null; + return `${formatDecimal( + max( + plus(collateralBalance.value, collateralAmountParsed.value), + "0" + ).toFixed() + )} ${collateralToken.value.symbol}`; + }); + + const debtValueBadge = computed(() => { + if (isZero(debtAmountParsed.value)) return null; + return `${formatDecimal( + max(plus(debtBalance.value, debtAmountParsed.value), "0").toFixed() + )} ${debtToken.value.symbol}`; + }); + + const { isMaxAmount: isCollateralMaxAmount } = useMaxAmountPassive( + collateralAmount, + collateralAmountParsed, + collateralDsaBalance + ); + + const { + stats, + liquidation, + liquidationPrice, + liquidationMaxPrice, + status: initialStatus, + annualPercentageRateTypes + } = useAaveV2Position({ + overridePosition: position => { + const changedPosition = { ...position }; + if (debtToken.value.key === position.key) { + changedPosition.borrow = max( + plus(position.borrow, debtAmountParsed.value), + "0" + ).toFixed(); + } + + if (collateralToken.value.key === position.key) { + changedPosition.supply = max( + plus(position.supply, collateralAmountParsed.value), + "0" + ).toFixed(); + } + + return changedPosition; + } + }); + + const status = computed(() => { + if (isZero(debtAmountParsed.value) && isZero(collateralAmountParsed.value)) + return initialStatus.value; + return max( + div(stats.value.totalBorrowInEth, stats.value.totalSupplyInEth), + "0" + ).toFixed(); + }); + + const errors = computed(() => { + const hasCollateralAmountValue = !!collateralAmount.value.length; + const hasDebtAmountValue = !!debtAmount.value.length; + + return { + collateralAmount: { + message: validateAmount(collateralAmountParsed.value), + show: hasCollateralAmountValue + }, + debtAmount: { + message: validateAmount(debtAmountParsed.value), + show: hasDebtAmountValue + }, + liquidation: { + message: validateLiquidation(status.value, liquidation.value), + show: hasCollateralAmountValue && hasDebtAmountValue + }, + auth: { + message: validateIsLoggedIn(!!account.value), + show: true + }, + liquidity: { + message: validateLiquidity( + debtAmountParsed.value, + availableLiquidity.value, + debtToken.value.symbol + ), + show: hasDebtAmountValue + } + }; + }); + + return { components, errors }; +} diff --git a/composables/strategies/aaveV2/index.ts b/composables/strategies/aaveV2/index.ts new file mode 100644 index 0000000..48b2580 --- /dev/null +++ b/composables/strategies/aaveV2/index.ts @@ -0,0 +1,6 @@ +import * as paybackAndWithdraw from './paybackAndWithdraw' +import * as depositAndBorrow from './depositAndBorrow' +export const strategies = [ + paybackAndWithdraw, + depositAndBorrow +] diff --git a/composables/strategies/aaveV2/paybackAndWithdraw.ts b/composables/strategies/aaveV2/paybackAndWithdraw.ts new file mode 100644 index 0000000..a15d808 --- /dev/null +++ b/composables/strategies/aaveV2/paybackAndWithdraw.ts @@ -0,0 +1,318 @@ +import { computed, ref, watch } from "@nuxtjs/composition-api"; +import { useAaveV2Position } from "~/composables/protocols/useAaveV2Position"; +import { useBalances } from "~/composables/useBalances"; +import { useBigNumber } from "~/composables/useBigNumber"; +import { useDSA } from "~/composables/useDSA"; +import { useFormatting } from "~/composables/useFormatting"; +import { useMaxAmountPassive } from "~/composables/useMaxAmountPassive"; +import { useParsing } from "~/composables/useParsing"; +import { useToken } from "~/composables/useToken"; +import { useValidators } from "~/composables/useValidators"; +import { useWeb3 } from "~/composables/useWeb3"; + +export const meta = { + id: "paybackAndWithdraw", + title: "Payback & Withdraw", + card: { + name: "Payback & Withdraw", + description: "Payback debt & withdraw collateral in a single txn.", + badge: null + }, + protocols: ["aaveV2"] +}; + +export function use() { + const description = computed(() => { + return ` +

This strategy executes:

+ + + + `; + }); + + const { account } = useWeb3(); + const { dsa } = useDSA(); + + const { position } = useAaveV2Position(); + const { getBalanceByAddress } = useBalances(); + + const { parseSafeFloat } = useParsing(); + const { + formatUsdMax, + formatUsd, + formatNumber, + formatDecimal + } = useFormatting(); + const { valInt, getTokenByKey } = useToken(); + + const { + validateAmount, + validateLiquidation, + validateIsLoggedIn + } = useValidators(); + const { isZero, minus, div, max, gt } = useBigNumber(); + + const debtAmount = ref(""); + const collateralAmount = ref(""); + const debtAmountParsed = computed(() => parseSafeFloat(debtAmount.value)); + const collateralAmountParsed = computed(() => + parseSafeFloat(collateralAmount.value) + ); + const rateType = ref(null); + + const borrowEnabled = position => position.borrowEnabled; + const debtTokens = computed(() => + position.value.data + .filter(borrowEnabled) + .filter(position => gt(position.borrow, 0)) + .map(token => token.key) + ); + + const debtToken = ref(null); + watch( + debtTokens, + tokens => { + if (tokens.includes(debtToken.value)) return; + + debtToken.value = getTokenByKey(debtTokens.value[0] || "dai"); + }, + { immediate: true } + ); + + const collateralTokens = computed(() => + position.value.data + .filter(position => gt(position.supply, 0)) + .map(token => token.key) + ); + + const collateralToken = ref(null); + watch( + collateralTokens, + tokens => { + if (tokens.includes(collateralToken.value)) return; + + collateralToken.value = getTokenByKey(collateralTokens.value[0] || "dai"); + }, + { immediate: true } + ); + + const collateralBalance = computed( + () => + position.value.data.find( + position => position.key === collateralToken.value.key + )?.supply || "0" + ); + + const debtPosition = computed(() => + position.value.data.find( + position => position.key === debtToken.value.key + ) + ); + const debtBalance = computed(() => { + if (rateType.value?.value === "stable") { + return debtPosition.value?.borrowStable || "0"; + } + return debtPosition.value?.borrow || "0"; + }); + + const stableBorrowEnabled = computed( + () => debtPosition.value?.stableBorrowEnabled + ); + const borrowStableRate = computed( + () => debtPosition.value?.borrowStableRate || "0" + ); + + const debtTokenBalance = computed(() => + getBalanceByAddress(debtToken.value?.address) + ); + + const debtValueBadge = computed(() => { + if (isZero(debtAmountParsed.value)) return null; + return `${formatDecimal( + max(minus(debtBalance.value, debtAmountParsed.value), "0").toFixed() + )} ${debtToken.value.symbol}`; + }); + + const collateralValueBadge = computed(() => { + if (isZero(collateralAmountParsed.value)) return null; + return `${formatDecimal( + max( + minus(collateralBalance.value, collateralAmountParsed.value), + "0" + ).toFixed() + )} ${collateralToken.value.symbol}`; + }); + + const components = computed(() => [ + { + type: "html", + minHeight: "96px", + html: description.value + }, + { + type: "rate-selection-aave", + onChange: type => (rateType.value = type), + value: rateType.value, + items: annualPercentageRateTypes.value, + borrowStableRate: borrowStableRate.value, + stableBorrowEnabled: stableBorrowEnabled.value + }, + { + type: "input-amount", + placeholder: `${debtToken.value.symbol} to Payback`, + value: debtAmount.value, + tokenKey: debtToken.value.key, + onInput: value => (debtAmount.value = value), + errorKey: "debtAmount" + }, + { + type: "input-amount", + placeholder: `${collateralToken.value.symbol} to Withdraw`, + value: collateralAmount.value, + tokenKey: collateralToken.value.key, + onInput: value => (collateralAmount.value = value), + errorKey: "collateralAmount" + }, + { + type: "heading", + text: "Projected Debt Position" + }, + { + type: "value-with-token-select", + label: "Collateral", + value: formatNumber(collateralBalance.value), + valueBadge: collateralValueBadge.value, + tokens: collateralTokens.value, + tokenKey: collateralToken.value.key, + onChange: tokenKey => (collateralToken.value = getTokenByKey(tokenKey)) + }, + { + type: "value-with-token-select", + label: "Debt", + value: formatNumber(debtBalance.value), + valueBadge: debtValueBadge.value, + tokens: debtTokens.value, + tokenKey: debtToken.value.key, + onChange: tokenKey => (debtToken.value = getTokenByKey(tokenKey)) + }, + { + type: "status", + liquidation: liquidation.value, + status: status.value + }, + { + type: "value", + label: "LIQUIDATION PRICE (IN ETH)", + value: `${formatUsdMax( + liquidationPrice.value, + liquidationMaxPrice.value + )} / ${formatUsd(liquidationMaxPrice.value)}` + }, + { + type: "button-submit", + label: "Payback & Withdraw", + onSubmit: async () => { + const debtTokenAddr = debtToken.value.address; + const collateralTokenAddr = collateralToken.value.address; + + const debtAmount = isDebtMaxAmount.value + ? dsa.value.maxValue + : valInt(debtAmountParsed.value, debtToken.value.decimals); + const collateralAmount = isCollateralMaxAmount.value + ? dsa.value.maxValue + : valInt( + collateralAmountParsed.value, + collateralToken.value.decimals + ); + + const spells = dsa.value.Spell(); + + const rateMode = rateType.value?.rateMode; + + spells.add({ + connector: "aave_v2", + method: "payback", + args: [debtTokenAddr, debtAmount, rateMode, 0, 0] + }); + + spells.add({ + connector: "aave_v2", + method: "withdraw", + args: [collateralTokenAddr, collateralAmount, 0, 0] + }); + + await dsa.value.cast({ + spells, + from: account.value + }); + } + } + ]); + + const { isMaxAmount: isDebtMaxAmount } = useMaxAmountPassive( + debtAmount, + debtAmountParsed, + debtBalance + ); + + const { isMaxAmount: isCollateralMaxAmount } = useMaxAmountPassive( + collateralAmount, + collateralAmountParsed, + collateralBalance + ); + + const { + stats, + maxLiquidation: liquidation, + liquidationPrice, + liquidationMaxPrice, + status: initialStatus, + annualPercentageRateTypes + } = useAaveV2Position({ + overridePosition: position => { + const changedPosition = { ...position }; + if (debtToken.value.key === position.key) { + changedPosition.borrow = max( + minus(position.borrow, debtAmountParsed.value), + "0" + ).toFixed(); + } + + if (collateralToken.value.key === position.key) { + changedPosition.supply = max( + minus(position.supply, collateralAmountParsed.value), + "0" + ).toFixed(); + } + + return changedPosition; + } + }); + + const status = computed(() => { + if (isZero(debtAmountParsed.value) && isZero(collateralAmountParsed.value)) + return initialStatus.value; + return max( + div(stats.value.totalBorrowInEth, stats.value.totalSupplyInEth), + "0" + ).toFixed(); + }); + + const errors = computed(() => { + const hasDebtAmountValue = !!debtAmount.value.length; + const hasCollateralAmountValue = !!collateralAmount.value.length; + // prettier-ignore + return { + debtAmount: { message: validateAmount(debtAmountParsed.value, debtTokenBalance.value, {msg: "You don't have enough balance to payback"}), show: hasDebtAmountValue }, + collateralAmount: { message: validateAmount(collateralAmountParsed.value, collateralBalance.value, {msg: "Your amount exceeds your supplied balance."}), show: hasCollateralAmountValue }, + liquidation: { message: validateLiquidation(status.value, liquidation.value), show: hasCollateralAmountValue && hasDebtAmountValue }, + auth: { message: validateIsLoggedIn(!!account.value), show: true }, + } + }); + + return { components, errors }; +} diff --git a/composables/strategies/useStrategy.ts b/composables/strategies/useStrategy.ts new file mode 100644 index 0000000..54a80b7 --- /dev/null +++ b/composables/strategies/useStrategy.ts @@ -0,0 +1,62 @@ +import { computed, ref, useRouter } from "@nuxtjs/composition-api"; +import { strategies as aaveV2 } from "./aaveV2"; + +const strategyId = ref(null); +const protocolStrategies = { + aaveV2 +}; + +export function useStrategy(protocol: string) { + const router = useRouter(); + + const strategies = computed(() => { + let strategies = protocolStrategies[protocol]; + if (!strategies) { + return []; + } + return strategies.filter( + s => s.meta.isShown === undefined || s.meta.isShown + ); + }); + + const strategy = computed(() => + strategies.value.find(strategy => strategy.meta.id === strategyId.value) + ); + + function select(id: string) { + strategyId.value = id; + + setImmediate(() => { + router.push({ hash: `strategy?protocol=${protocol}` }); + }); + } + + function use() { + if (!strategy.value) { + throw new Error("No strategy is chosen"); + } + + if (!strategy.value.use) { + throw new Error("Invalid strategy: Strategy has no use function"); + } + + if (typeof strategy.value.use !== "function") { + throw new TypeError("Invalid strategy: `use` is no function"); + } + + return strategy.value.use(); + } + + function show(id: string) { + strategyId.value = id; + } + + return { + strategies, + strategy, + meta: computed(() => strategy.value?.meta), + show, + select, + use + }; +} diff --git a/composables/useBalances.ts b/composables/useBalances.ts index 6703772..36b279a 100644 --- a/composables/useBalances.ts +++ b/composables/useBalances.ts @@ -131,6 +131,7 @@ export function useBalances() { return { balances, fetchBalances, + getBalanceByAddress, getBalanceByKey, getBalanceRawByKey, balanceTotal, diff --git a/composables/useMaxAmountPassive.ts b/composables/useMaxAmountPassive.ts new file mode 100644 index 0000000..9a54736 --- /dev/null +++ b/composables/useMaxAmountPassive.ts @@ -0,0 +1,33 @@ +//@ts-nocheck +import { computed, ref, watch } from '@nuxtjs/composition-api' +import { useBigNumber } from './useBigNumber' + +/** + * Caps amount at maxAmount and returns isMaxAmount as true when capped. + * Keeps amount and maxAmount in sync as long as amount is not updated with a smaller value then maxAmount. + * + * @param {import('@nuxtjs/composition-api').Ref} amountRef + * @param {import('@nuxtjs/composition-api').Ref} amountParsedRef + * @param {import('@nuxtjs/composition-api').Ref} maxAmountRef + */ +export function useMaxAmountPassive(amountRef, amountParsedRef, maxAmountRef) { + const { toBN, eq, gte } = useBigNumber() + const syncAmount = ref(false) + + watch( + [amountRef, maxAmountRef], + ([amount, maxAmount], [oldAmount] = [null]) => { + if (!eq(amount, oldAmount)) { + syncAmount.value = false + } + + if (syncAmount.value || gte(amountParsedRef.value, maxAmountRef.value)) { + syncAmount.value = true + amountRef.value = toBN(maxAmount).toFixed() + } + }, + { immediate: true } + ) + + return { isMaxAmount: computed(() => syncAmount.value) } +} diff --git a/composables/useSidebar.ts b/composables/useSidebar.ts index 98fd763..f640955 100644 --- a/composables/useSidebar.ts +++ b/composables/useSidebar.ts @@ -35,11 +35,16 @@ import SidebarLiquityTroveWithdraw from '~/components/sidebar/context/liquity/Si import SidebarLiquityTroveBorrow from '~/components/sidebar/context/liquity/SidebarLiquityTroveBorrow.vue' import SidebarLiquityTrovePayback from '~/components/sidebar/context/liquity/SidebarLiquityTrovePayback.vue' +import SidebarStrategySelection from '~/components/sidebar/context/strategy/SidebarStrategySelection.vue' +import SidebarStrategy from '~/components/sidebar/context/strategy/SidebarStrategy.vue' const sidebars = { "#overview" : {component: SidebarOverview, back : false, close : true }, "#deposit-overview": {component: SidebarDepositOverview, back: { hash: 'overview' } }, '#withdraw-token': { component: SidebarWithdraw, back: { hash: 'overview' } }, + '#strategies': { component: SidebarStrategySelection }, + '#strategy': { component: SidebarStrategy, back: { hash: 'strategies' } }, + "/aave-v2": { component: null }, "/aave-v2#supply": { component: SidebarAaveV2Supply }, "/aave-v2#borrow": { component: SidebarAaveV2Borrow }, diff --git a/package.json b/package.json index ebbc75f..092e1cb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@nuxtjs/composition-api": "^0.24.7", "@portis/web3": "^4.0.5", "@tailwindcss/forms": "^0.3.3", + "@tailwindcss/typography": "^0.4.1", "@vueuse/core": "^5.1.4", "@walletconnect/web3-provider": "^1.4.1", "bignumber.js": "^9.0.1", diff --git a/pages/aave-v2.vue b/pages/aave-v2.vue index e1c64c0..06a18cd 100644 --- a/pages/aave-v2.vue +++ b/pages/aave-v2.vue @@ -10,18 +10,43 @@ -
-
+
+
- +
+ +
+

Aave v2

-

Aave v2

+ + + Strategies + + + + +
@@ -166,12 +191,14 @@ import { useStatus } from "~/composables/useStatus"; import { useBigNumber } from "~/composables/useBigNumber"; import CardAave from "~/components/protocols/CardAave.vue"; import AaveIcon from "~/assets/icons/aave.svg?inline"; +import ButtonCTAOutlined from "~/components/common/input/ButtonCTAOutlined.vue"; export default defineComponent({ components: { BackIcon, CardAave, - AaveIcon + AaveIcon, + ButtonCTAOutlined }, setup() { const { diff --git a/pages/mainnet/aave-v2.vue b/pages/mainnet/aave-v2.vue deleted file mode 100644 index 46501dc..0000000 --- a/pages/mainnet/aave-v2.vue +++ /dev/null @@ -1,216 +0,0 @@ - - - diff --git a/tailwind.config.js b/tailwind.config.js index 240fc3e..f6a40cf 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -120,5 +120,6 @@ module.exports = { }, plugins: [ require('@tailwindcss/forms'), + require('@tailwindcss/typography'), ], } diff --git a/yarn.lock b/yarn.lock index 35f518d..da153b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1707,6 +1707,16 @@ dependencies: mini-svg-data-uri "^1.2.3" +"@tailwindcss/typography@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.4.1.tgz#51ddbceea6a0ee9902c649dbe58871c81a831212" + integrity sha512-ovPPLUhs7zAIJfr0y1dbGlyCuPhpuv/jpBoFgqAc658DWGGrOBWBMpAWLw2KlzbNeVk4YBJMzue1ekvIbdw6XA== + dependencies: + lodash.castarray "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + lodash.uniq "^4.5.0" + "@types/anymatch@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-3.0.0.tgz#c95ff14401dbb2869913afac3935af4ad0d37f1a" @@ -7277,11 +7287,21 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.castarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" + integrity sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + lodash.kebabcase@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" @@ -7292,6 +7312,11 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.template@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"