assembly/composables/strategies/aaveV2/depositAndBorrow.ts
2021-08-21 23:13:10 +03:00

330 lines
9.0 KiB
TypeScript

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 `
<p>This strategy executes:</p>
<ul>
<li>Deposit collateral</li>
<li>Borrow Debt</li>
</ul>
`;
});
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 };
}