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"
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|