assembly/core/strategies/protocols/aave-v2/deposit-and-borrow.ts

267 lines
7.1 KiB
TypeScript
Raw Permalink Normal View History

2021-08-26 16:38:31 +00:00
import BigNumber from "bignumber.js";
2021-08-25 11:13:23 +00:00
import {
defineStrategy,
2021-09-05 11:46:39 +00:00
defineStrategyComponent,
StrategyComponentType,
2021-08-25 11:13:23 +00:00
StrategyProtocol
} from "../../helpers";
2021-08-22 11:27:02 +00:00
export default defineStrategy({
2021-08-25 11:13:23 +00:00
protocol: StrategyProtocol.AAVE_V2,
2021-08-22 11:27:02 +00:00
name: "Deposit & Borrow",
description: "Deposit collateral & borrow asset in a single txn.",
2021-08-25 11:13:23 +00:00
details: `<p class="text-center">This strategy executes:</p>
<ul>
<li>Deposit collateral</li>
<li>Borrow Debt</li>
</ul>`,
2021-09-05 18:31:23 +00:00
submitText: "Deposit & Borrow",
2021-09-04 16:50:56 +00:00
2021-08-22 11:27:02 +00:00
author: "Instadapp Team",
2021-08-26 16:38:31 +00:00
variables: {
collateralTokenKey: "eth",
debtTokenKey: "dai",
2021-08-26 18:34:27 +00:00
debtRateMode: 2
2021-08-26 16:38:31 +00:00
},
2021-09-05 11:46:39 +00:00
components: [
defineStrategyComponent({
type: StrategyComponentType.INPUT_WITH_TOKEN,
2021-08-26 16:38:31 +00:00
name: "Collateral",
2021-09-05 11:46:39 +00:00
placeholder: ({ component: input }) =>
2021-08-26 16:38:31 +00:00
input.token ? `${input.token.symbol} to Deposit` : "",
2021-09-05 11:46:39 +00:00
validate: ({ component: input, dsaBalances, toBN }) => {
2021-08-22 11:27:02 +00:00
if (!input.token) {
2021-08-26 16:38:31 +00:00
return "Collateral token is required";
2021-08-22 11:27:02 +00:00
}
2021-08-25 11:13:23 +00:00
if (!input.value) {
2021-08-26 16:38:31 +00:00
return "Collateral amount is required";
2021-08-22 12:54:23 +00:00
}
2021-08-25 11:13:23 +00:00
2021-08-26 18:34:27 +00:00
const collateralBalance = toBN(
2021-08-26 16:38:31 +00:00
dsaBalances[input.token.address]?.balance
);
2021-08-26 18:34:27 +00:00
if (toBN(collateralBalance).lt(input.value)) {
2021-08-26 16:38:31 +00:00
const collateralBalanceFormatted = collateralBalance.toFixed(2);
return `Your amount exceeds your maximum limit of ${collateralBalanceFormatted} ${input.token.symbol}`;
}
2021-08-22 18:32:28 +00:00
},
2021-08-26 16:38:31 +00:00
defaults: ({ getTokenByKey, variables }) => ({
token: getTokenByKey?.(variables.collateralTokenKey)
2021-08-25 11:13:23 +00:00
})
2021-08-22 15:45:37 +00:00
}),
2021-08-26 16:38:31 +00:00
2021-09-05 11:46:39 +00:00
defineStrategyComponent({
type: StrategyComponentType.INPUT_WITH_TOKEN,
2021-08-26 16:38:31 +00:00
name: "Debt",
2021-09-05 11:46:39 +00:00
placeholder: ({ component: input }) =>
2021-08-26 16:38:31 +00:00
input.token ? `${input.token.symbol} to Borrow` : "",
2021-09-05 11:46:39 +00:00
validate: ({ component: input }) => {
2021-08-25 11:13:23 +00:00
if (!input.token) {
2021-08-26 16:38:31 +00:00
return "Debt token is required";
2021-08-25 11:13:23 +00:00
}
if (!input.value) {
2021-08-26 16:38:31 +00:00
return "Debt amount is required";
2021-08-25 11:13:23 +00:00
}
},
2021-08-26 16:38:31 +00:00
defaults: ({ getTokenByKey, variables }) => ({
token: getTokenByKey?.(variables.debtTokenKey)
2021-08-23 23:07:53 +00:00
})
2021-09-05 18:31:23 +00:00
}),
defineStrategyComponent({
type: StrategyComponentType.HEADING,
name: "Projected Debt Position"
}),
defineStrategyComponent({
type: StrategyComponentType.STATUS,
name: "Status",
update: ({ position, component, components, toBN }) => {
if (
toBN(components[0].value).isZero() &&
toBN(components[1].value).isZero()
) {
return;
}
2021-08-22 11:27:02 +00:00
2021-09-05 18:31:23 +00:00
if (!position) {
return;
}
const newPositionData = changedPositionData(position, components);
const stats = calculateStats(newPositionData);
2021-08-26 18:34:27 +00:00
2021-09-05 18:31:23 +00:00
component.liquidation = BigNumber.max(
toBN(stats.totalMaxBorrowLimitInEth).div(stats.totalSupplyInEth),
2021-08-26 18:34:27 +00:00
"0"
).toFixed();
2021-09-05 18:31:23 +00:00
component.status = BigNumber.max(
toBN(stats.totalBorrowInEth).div(stats.totalSupplyInEth),
2021-08-26 18:34:27 +00:00
"0"
).toFixed();
}
2021-09-05 18:31:23 +00:00
}),
defineStrategyComponent({
type: StrategyComponentType.VALUE,
name: "LIQUIDATION PRICE (IN ETH)",
value: "-",
update: ({ position, component, components, toBN, formatting }) => {
if (!position) {
return;
2021-08-26 18:34:27 +00:00
}
2021-09-05 18:31:23 +00:00
const newPositionData = changedPositionData(position, components);
const initialStats = calculateStats(position.data);
const newStats = calculateStats(newPositionData);
2021-08-26 18:34:27 +00:00
2021-09-05 18:31:23 +00:00
const stats =
toBN(components[0].value).isZero() &&
toBN(components[1].value).isZero()
? initialStats
: newStats;
2021-08-26 18:34:27 +00:00
2021-09-05 18:31:23 +00:00
let liquidationPrice = "0";
if (!toBN(stats.ethSupplied).isZero()) {
liquidationPrice = BigNumber.max(
toBN(stats.totalBorrowInEth)
.div(stats.totalMaxLiquidationLimitInEth)
.times(position.ethPriceInUsd),
"0"
).toFixed();
}
component.value = `${formatting.formatUsdMax(
liquidationPrice,
position.ethPriceInUsd
)} / ${formatting.formatUsd(position.ethPriceInUsd)}`;
2021-08-26 18:34:27 +00:00
}
2021-09-05 18:31:23 +00:00
})
],
validate: async ({ position, components: inputs, toBN }) => {
if (toBN(inputs[0].value).isZero() && toBN(inputs[1].value).isZero()) {
return;
}
const newPositionData = changedPositionData(position, inputs);
const stats = calculateStats(newPositionData);
2021-08-26 18:34:27 +00:00
let liquidation = "0";
if (!toBN(stats.totalSupplyInEth).isZero()) {
liquidation = BigNumber.max(
toBN(stats.totalMaxBorrowLimitInEth).div(stats.totalSupplyInEth),
"0"
).toFixed();
}
const status = BigNumber.max(
toBN(stats.totalBorrowInEth).div(stats.totalSupplyInEth),
"0"
);
if (status.gt(toBN(liquidation).minus("0.0001"))) {
return "Position will liquidate.";
}
},
2021-09-05 18:31:23 +00:00
spells: async ({
components: inputs,
convertTokenAmountToWei,
variables
}) => {
2021-08-22 15:45:37 +00:00
return [
{
connector: "aave_v2",
method: "deposit",
2021-08-25 11:13:23 +00:00
args: [
inputs[0].token.address,
2021-08-26 16:38:31 +00:00
convertTokenAmountToWei(inputs[0].value, inputs[0].token.decimals),
2021-08-25 11:13:23 +00:00
0,
0
]
2021-08-22 15:45:37 +00:00
},
{
connector: "aave_v2",
method: "borrow",
2021-08-25 11:13:23 +00:00
args: [
inputs[1].token.address,
2021-08-26 16:38:31 +00:00
convertTokenAmountToWei(inputs[1].value, inputs[1].token.decimals),
variables.debtRateMode,
2021-08-25 11:13:23 +00:00
0,
0
]
2021-08-22 15:45:37 +00:00
}
];
2021-08-22 11:27:02 +00:00
}
});
2021-09-05 18:31:23 +00:00
const changedPositionData = (position, inputs) => {
return position.data.map(position => {
const changedPosition = { ...position };
if (inputs[1].token.key === position.key) {
changedPosition.borrow = BigNumber.max(
new BigNumber(position.borrow).plus(inputs[1].value || "0"),
"0"
).toFixed();
}
if (inputs[0].token.key === position.key) {
changedPosition.supply = BigNumber.max(
new BigNumber(position.supply).plus(inputs[0].value || "0"),
"0"
).toFixed();
}
return changedPosition;
});
};
const calculateStats = positionData => {
return positionData.reduce(
(
stats,
{ key, supply, borrow, borrowStable, priceInEth, factor, liquidation }
) => {
if (key === "eth") {
stats.ethSupplied = supply;
}
const borrowTotal = new BigNumber(borrow).plus(borrowStable);
stats.totalSupplyInEth = new BigNumber(supply)
.times(priceInEth)
.plus(stats.totalSupplyInEth)
.toFixed();
stats.totalBorrowInEth = new BigNumber(borrowTotal)
.times(priceInEth)
.plus(stats.totalBorrowInEth)
.toFixed();
stats.totalMaxBorrowLimitInEth = new BigNumber(priceInEth)
.times(factor)
.times(supply)
.plus(stats.totalMaxBorrowLimitInEth)
.toFixed();
stats.totalMaxLiquidationLimitInEth = new BigNumber(priceInEth)
.times(liquidation)
.times(supply)
.plus(stats.totalMaxLiquidationLimitInEth)
.toFixed();
return stats;
},
{
totalSupplyInEth: "0",
totalBorrowInEth: "0",
totalMaxBorrowLimitInEth: "0",
totalMaxLiquidationLimitInEth: "0"
}
);
};