This commit is contained in:
Georges KABBOUCHI 2021-09-05 02:12:21 +03:00
parent 52ebdbfb3f
commit 90d0379510
8 changed files with 466 additions and 12 deletions

View File

@ -27,7 +27,7 @@ export const trove = ref<any>({
liquidation: "0"
});
const troveTypes = ref([
export const troveTypes = ref([
{
totalCollateral: "0",
price: "0",
@ -42,7 +42,7 @@ const troveTypes = ref([
}
]);
const troveOverallDetails = computed(() =>
export const troveOverallDetails = computed(() =>
troveTypes.value.find(t => t.tokenKey === trove.value.tokenKey)
);

View File

@ -14,7 +14,7 @@ import {
import { position as aaveV2Position } from "./protocols/useAaveV2Position";
import { position as compoundPosition } from "./protocols/useCompoundPosition";
import { vault as makerPosition } from "./protocols/useMakerdaoPosition";
import { trove as liquityPosition } from "./protocols/useLiquityPosition";
import { trove as liquityPosition, troveTypes, troveOverallDetails } from "./protocols/useLiquityPosition";
import { useBalances } from "./useBalances";
import { useDSA } from "./useDSA";
import useEventBus from "./useEventBus";
@ -70,6 +70,7 @@ export function useStrategy(defineStrategy: DefineStrategy) {
watchEffect(() => {
let position = null;
let positionExtra = {}
if (strategy.schema.protocol == StrategyProtocol.AAVE_V2) {
position = aaveV2Position.value;
@ -79,6 +80,9 @@ export function useStrategy(defineStrategy: DefineStrategy) {
position = compoundPosition.value;
} else if (strategy.schema.protocol == StrategyProtocol.LIQUITY) {
position = liquityPosition.value;
positionExtra["troveTypes"] = troveTypes.value;
positionExtra["troveOverallDetails"] = troveOverallDetails.value;
}
strategy.setProps({
@ -86,6 +90,7 @@ export function useStrategy(defineStrategy: DefineStrategy) {
getTokenByKey,
toBN,
position,
positionExtra,
tokenIdMapping
});
});

View File

@ -19,6 +19,7 @@ export interface IStrategyContext {
convertTokenAmountToWei?: (value: any, decimals: any) => string;
getTokenByKey?: (key: string) => IStrategyToken;
position?: any;
positionExtra?: { [key: string]: any };
variables?: { [key: string]: any };
toBN?: (value: any) => BigNumber;
tokenIdMapping?: typeof tokenIdMapping;
@ -30,6 +31,7 @@ export interface IStrategyToken {
key: string;
symbol: string;
balance: string;
decimals: string;
// supply: string;
// borrow: string;

View File

@ -1,9 +1,11 @@
import AaveV2 from "./protocols/aave-v2"
import Compound from "./protocols/compound"
import Liquity from "./protocols/liquity"
export const protocolStrategies = {
aaveV2 : AaveV2,
compound : Compound,
liquity : Liquity,
}
export * from "./helpers"

View File

@ -0,0 +1,216 @@
import BigNumber from "bignumber.js";
import abis from "~/constant/abis";
import addresses from "~/constant/addresses";
import {
defineStrategy,
defineInput,
StrategyInputType,
StrategyProtocol
} from "../../helpers";
export default defineStrategy({
protocol: StrategyProtocol.LIQUITY,
name: "Deposit & Borrow",
description: "Deposit collateral & borrow asset in a single txn.",
details: `<p class="text-center">This strategy executes:</p>
<ul>
<li>Deposit ETH as collateral</li>
<li>Borrow LUSD as Debt</li>
</ul>`,
submitText: "Deposit & Borrow",
author: "Instadapp Team",
variables: {
collateralTokenKey: "eth",
debtTokenKey: "lusd"
},
inputs: [
defineInput({
type: StrategyInputType.INPUT_WITH_TOKEN,
name: "Collateral",
placeholder: ({ input }) =>
input.token ? `${input.token.symbol} to Deposit` : "",
validate: ({ input, dsaBalances, toBN }) => {
if (!input.token) {
return "Collateral token is required";
}
if (!input.value) {
return "Collateral amount is required";
}
const collateralBalance = toBN(
dsaBalances[input.token.address]?.balance
);
if (toBN(collateralBalance).lt(input.value)) {
const collateralBalanceFormatted = collateralBalance.toFixed(2);
return `Your amount exceeds your maximum limit of ${collateralBalanceFormatted} ${input.token.symbol}`;
}
},
defaults: ({ getTokenByKey, variables }) => ({
token: getTokenByKey?.(variables.collateralTokenKey)
})
}),
defineInput({
type: StrategyInputType.INPUT_WITH_TOKEN,
name: "Debt",
placeholder: ({ input }) =>
input.token ? `${input.token.symbol} to Borrow` : "",
validate: ({ input, toBN, position, positionExtra }) => {
if (!input.token) {
return "Debt token is required";
}
if (!input.value) {
return "Debt amount is required";
}
const troveOverallDetails = positionExtra["troveOverallDetails"];
const borrowFeeAmount = toBN(input.value)
.times(troveOverallDetails.borrowFee)
.toFixed();
const debtInputAmountWithFee = toBN(input.value)
.plus(borrowFeeAmount)
.toFixed();
const changedDebt = toBN(position.debt).plus(debtInputAmountWithFee);
const totalDebt = toBN(changedDebt).plus(
troveOverallDetails.liquidationReserve
);
if (totalDebt.isZero())
return `Minimum total debt requirement is ${troveOverallDetails.minDebt} LUSD`;
if (totalDebt.lt(troveOverallDetails.minDebt) && totalDebt.gt("0")) {
return `Minimum total debt requirement is ${troveOverallDetails.minDebt} LUSD`;
}
},
defaults: ({ getTokenByKey, variables }) => ({
token: getTokenByKey?.(variables.debtTokenKey)
})
})
],
validate: async ({ position, positionExtra, inputs, toBN }) => {
if (toBN(inputs[0].value).isZero() && toBN(inputs[1].value).isZero()) {
return;
}
const troveOpened =
!toBN(position.collateral).isZero() && !toBN(position.debt).isZero();
if (!troveOpened) {
return "You should open new trove first";
}
// const troveOverallDetails = positionExtra["troveOverallDetails"];
// const status =
// toBN(inputs[0].value).isZero() && !toBN(inputs[1].value).isZero()
// ? toBN("1.1")
// : toBN(inputs[0].value)
// .times(position.price)
// .div(inputs[1].value);
// console.log(status.toFixed(), troveOverallDetails.liquidation);
// if (status.gt(toBN(troveOverallDetails.liquidation).minus("0.0001"))) {
// return "Position will liquidate.";
// }
},
spells: async ({
inputs,
position,
positionExtra,
getTokenByKey,
convertTokenAmountToWei,
web3,
toBN
}) => {
const troveOverallDetails = positionExtra["troveOverallDetails"];
const collateralToken = getTokenByKey("eth");
const collateralInWei = convertTokenAmountToWei(
position.collateral,
collateralToken.decimals
);
const depositAmountInWei = convertTokenAmountToWei(
inputs[0].value,
inputs[0].token.decimals
);
const totalDepositAmountInWei = toBN(depositAmountInWei)
.plus(collateralInWei)
.toFixed();
const borrowFeeAmount = toBN(inputs[1].value)
.times(troveOverallDetails.borrowFee)
.toFixed();
const debtInputAmountWithFee = toBN(inputs[1].value)
.plus(borrowFeeAmount)
.toFixed();
const changedDebt = toBN(position.debt)
.plus(debtInputAmountWithFee)
.toFixed();
const borrowAmountInWei = convertTokenAmountToWei(
inputs[1].value,
inputs[1].value.decimals
);
const totalBorrowAmountInWei = convertTokenAmountToWei(
changedDebt,
inputs[1].value.decimals
);
const withdrawAmountInWei = "0";
const paybackAmountInWei = "0";
const liquityInstance = new web3.eth.Contract(
abis.resolver.liquity as any,
addresses.mainnet.resolver.liquity
);
const {
upperHint,
lowerHint
} = await liquityInstance.methods
.getTrovePositionHints(
totalDepositAmountInWei.toString(),
totalBorrowAmountInWei.toString(),
0,
0
)
.call();
const getIds = [0, 0, 0, 0];
const setIds = [0, 0, 0, 0];
return [
{
connector: "LIQUITY-A",
method: "adjust",
args: [
toBN(troveOverallDetails.borrowFee)
.times("100")
.times("1e18")
.toFixed(),
depositAmountInWei,
withdrawAmountInWei,
borrowAmountInWei,
paybackAmountInWei,
upperHint,
lowerHint,
getIds,
setIds
]
}
];
}
});

View File

@ -0,0 +1,8 @@
import depositAndBorrow from "./deposit-and-borrow"
import paybackAndWithdraw from "./payback-and-withdraw"
export default [
depositAndBorrow,
paybackAndWithdraw,
]

View File

@ -0,0 +1,194 @@
import BigNumber from "bignumber.js";
import abis from "~/constant/abis";
import addresses from "~/constant/addresses";
import {
defineStrategy,
defineInput,
StrategyInputType,
StrategyProtocol
} from "../../helpers";
export default defineStrategy({
protocol: StrategyProtocol.LIQUITY,
name: "Payback & Withdraw",
description: "Payback debt & withdraw collateral in a single txn.",
author: "Instadapp Team",
submitText: "Payback & Withdraw",
details: `<p class="text-center">This strategy executes:</p>
<ul>
<li>Payback debt</li>
<li>Withdraw collateral</li>
</ul>`,
inputs: [
defineInput({
type: StrategyInputType.INPUT_WITH_TOKEN,
name: "Debt",
placeholder: ({ input }) =>
input.token ? `${input.token.symbol} to Payback` : "",
validate: ({ input, toBN, position, positionExtra }) => {
if (!input.token) {
return "Debt token is required";
}
if (!input.value) {
return "Debt amount is required";
}
const troveOverallDetails = positionExtra["troveOverallDetails"];
const borrowFeeAmount = toBN(input.value)
.times(troveOverallDetails.borrowFee)
.toFixed();
const debtInputAmountWithFee = toBN(input.value)
.plus(borrowFeeAmount)
.toFixed();
const changedDebt = toBN(position.debt).plus(debtInputAmountWithFee);
const totalDebt = toBN(changedDebt).plus(
troveOverallDetails.liquidationReserve
);
if (totalDebt.isZero())
return `Minimum total debt requirement is ${troveOverallDetails.minDebt} LUSD`;
if (totalDebt.lt(troveOverallDetails.minDebt) && totalDebt.gt("0")) {
return `Minimum total debt requirement is ${troveOverallDetails.minDebt} LUSD`;
}
},
defaults: ({ getTokenByKey }) => ({
token: getTokenByKey?.("lusd")
})
}),
defineInput({
type: StrategyInputType.INPUT_WITH_TOKEN,
name: "Collateral",
placeholder: ({ input }) =>
input.token ? `${input.token.symbol} to Withdraw` : "",
validate: ({ input, dsaBalances, toBN }) => {
if (!input.token) {
return "Collateral token is required";
}
if (!input.value) {
return "Collateral amount is required";
}
const collateralBalance = toBN(
dsaBalances[input.token.address]?.balance
);
if (toBN(collateralBalance).lt(input.value)) {
const collateralBalanceFormatted = collateralBalance.toFixed(2);
return `Your amount exceeds your maximum limit of ${collateralBalanceFormatted} ${input.token.symbol}`;
}
},
defaults: ({ getTokenByKey }) => ({
token: getTokenByKey?.("eth")
})
})
],
validate: async ({ position, inputs, toBN }) => {
if (toBN(inputs[0].value).isZero() && toBN(inputs[1].value).isZero()) {
return;
}
const troveOpened =
!toBN(position.collateral).isZero() && !toBN(position.debt).isZero();
if (!troveOpened) {
return "You should open new trove first";
}
},
spells: async ({
inputs,
position,
positionExtra,
getTokenByKey,
convertTokenAmountToWei,
web3,
toBN
}) => {
const troveOverallDetails = positionExtra["troveOverallDetails"];
const collateralToken = getTokenByKey("eth");
const collateralInWei = convertTokenAmountToWei(
position.collateral,
collateralToken.decimals
);
const withdrawAmountInWei = convertTokenAmountToWei(
inputs[0].value,
inputs[0].token.decimals
);
const totalDepositAmountInWei = toBN(withdrawAmountInWei)
.plus(collateralInWei)
.toFixed();
const borrowFeeAmount = toBN(inputs[1].value)
.times(troveOverallDetails.borrowFee)
.toFixed();
const debtInputAmountWithFee = toBN(inputs[1].value)
.plus(borrowFeeAmount)
.toFixed();
const changedDebt = toBN(position.debt)
.plus(debtInputAmountWithFee)
.toFixed();
const paybackAmountInWei = convertTokenAmountToWei(
inputs[1].value,
inputs[1].value.decimals
);
const totalBorrowAmountInWei = convertTokenAmountToWei(
changedDebt,
inputs[1].value.decimals
);
const depositAmountInWei = "0";
const borrowAmountInWei = "0";
const liquityInstance = new web3.eth.Contract(
abis.resolver.liquity as any,
addresses.mainnet.resolver.liquity
);
const {
upperHint,
lowerHint
} = await liquityInstance.methods
.getTrovePositionHints(
totalDepositAmountInWei.toString(),
totalBorrowAmountInWei.toString(),
0,
0
)
.call();
const getIds = [0, 0, 0, 0];
const setIds = [0, 0, 0, 0];
return [
{
connector: "LIQUITY-A",
method: "adjust",
args: [
toBN(troveOverallDetails.borrowFee)
.times("100")
.times("1e18")
.toFixed(),
depositAmountInWei,
withdrawAmountInWei,
borrowAmountInWei,
paybackAmountInWei,
upperHint,
lowerHint,
getIds,
setIds
]
}
];
}
});

View File

@ -10,18 +10,43 @@
</nuxt-link>
</div>
<div class="mt-10 flex items-center">
<div
style="background: radial-gradient(42.15% 42.15% at 48.94% 48.94%, #D6DAE0 75.67%, #F0F3F9 100%), #C4C4C4;"
class="w-16 h-16 rounded-full flex items-center justify-center border border-[#CCDCF3]"
>
<div class="mt-10 flex items-center justify-between">
<div class="flex items-center">
<div
class="w-12 h-12 rounded-full flex items-center justify-center bg-[#1874FF]"
style="background: radial-gradient(42.15% 42.15% at 48.94% 48.94%, #D6DAE0 75.67%, #F0F3F9 100%), #C4C4C4;"
class="w-16 h-16 rounded-full flex items-center justify-center border border-[#CCDCF3]"
>
<LiquityIcon class="w-8 h-8 text-white" />
<div
class="w-12 h-12 rounded-full flex items-center justify-center bg-[#1874FF]"
>
<LiquityIcon class="w-8 h-8 text-white" />
</div>
</div>
<h1 class="ml-4 text-primary-black text-2xl font-semibold">Liquity</h1>
</div>
<h1 class="ml-4 text-primary-black text-2xl font-semibold">Liquity</h1>
<ButtonCTAOutlined
class="px-4 h-9 w-[173px]"
@click="$router.push({ hash: 'strategies?protocol=liquity' })"
>
Strategies
<svg
class="ml-auto"
width="11"
height="10"
viewBox="0 0 11 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.3815 8.76139C4.38149 8.99703 4.64068 9.1407 4.8405 9.01581L10.859 5.25425C11.047 5.13675 11.047 4.86295 10.859 4.74545L4.84088 0.984069C4.64108 0.859187 4.38189 1.00283 4.38188 1.23845L4.38179 2.97869C4.38178 3.14437 4.24747 3.27867 4.08179 3.27867L1.23894 3.27867C1.07325 3.27867 0.93894 3.41299 0.93894 3.57867L0.93894 6.42096C0.93894 6.58664 1.07325 6.72096 1.23894 6.72096L4.08159 6.72096C4.24728 6.72096 4.3816 6.85528 4.38159 7.02097L4.3815 8.76139Z"
fill="#1874FF"
/>
</svg>
</ButtonCTAOutlined>
</div>
<div class="mt-10">
@ -150,6 +175,7 @@ import { useFormatting } from "~/composables/useFormatting";
import { useStatus } from "~/composables/useStatus";
import { useBigNumber } from "~/composables/useBigNumber";
import CardLiquityTrove from "~/components/protocols/liquity/CardLiquityTrove.vue";
import ButtonCTAOutlined from "~/components/common/input/ButtonCTAOutlined.vue";
export default defineComponent({
components: {
@ -159,7 +185,8 @@ export default defineComponent({
SVGAdd,
SVGBalance,
SVGPercent,
CardLiquityTrove
CardLiquityTrove,
ButtonCTAOutlined,
},
setup() {
const router = useRouter();