Merge branch 'master' into liquity-stability-pool

This commit is contained in:
Georges KABBOUCHI 2021-10-08 18:11:51 +03:00
commit b83b41ebd6
17 changed files with 2876 additions and 700 deletions

400
abis/read/yearnV2.json Normal file
View File

@ -0,0 +1,400 @@
[
{
"inputs": [
{
"internalType": "contract YearnV2Interface",
"name": "vault",
"type": "address"
}
],
"name": "getAvailableDepositLimit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "contract YearnV2Interface",
"name": "vault",
"type": "address"
}
],
"name": "getBalance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "contract YearnV2Interface",
"name": "vault",
"type": "address"
}
],
"name": "getExpectedShareValue",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address[]",
"name": "wantAddresses",
"type": "address[]"
}
],
"name": "getPositions",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "vaultLatestVersion",
"type": "address"
},
{
"internalType": "address",
"name": "vault",
"type": "address"
},
{
"internalType": "address",
"name": "want",
"type": "address"
},
{
"internalType": "uint256",
"name": "pricePerShare",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "availableDepositLimit",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalAssets",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "balanceOf",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "wantBalanceOf",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expectedShareValue",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "decimals",
"type": "uint256"
},
{
"internalType": "bool",
"name": "isDeprecated",
"type": "bool"
},
{
"internalType": "bool",
"name": "emergencyShutdown",
"type": "bool"
}
],
"internalType": "struct Helpers.VaultData[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address[]",
"name": "wantAddresses",
"type": "address[]"
}
],
"name": "getPositionsForLatest",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "vaultLatestVersion",
"type": "address"
},
{
"internalType": "address",
"name": "vault",
"type": "address"
},
{
"internalType": "address",
"name": "want",
"type": "address"
},
{
"internalType": "uint256",
"name": "pricePerShare",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "availableDepositLimit",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalAssets",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "balanceOf",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "wantBalanceOf",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expectedShareValue",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "decimals",
"type": "uint256"
},
{
"internalType": "bool",
"name": "isDeprecated",
"type": "bool"
},
{
"internalType": "bool",
"name": "emergencyShutdown",
"type": "bool"
}
],
"internalType": "struct Helpers.VaultData[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract YearnV2Interface",
"name": "vault",
"type": "address"
}
],
"name": "getPricePerShare",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getRegistry",
"outputs": [
{
"internalType": "contract YearnRegistryInterface",
"name": "",
"type": "address"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract YearnV2Interface",
"name": "vault",
"type": "address"
}
],
"name": "getTotalAssets",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract YearnV2Interface",
"name": "vault",
"type": "address"
}
],
"name": "isEmergencyShutdown",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "want",
"type": "address"
}
],
"name": "isWantSupported",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "want",
"type": "address"
}
],
"name": "latestForWant",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "want",
"type": "address"
}
],
"name": "listVaultsForWant",
"outputs": [
{
"internalType": "address[]",
"name": "vaultAddresses",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "want",
"type": "address"
}
],
"name": "numVaultsForWant",
"outputs": [
{
"internalType": "uint256",
"name": "numVaults",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]

7
assets/icons/yearn.svg Normal file
View File

@ -0,0 +1,7 @@
<svg viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.375 11.375C11.375 10.4788 12.1038 9.75 13 9.75C13.8962 9.75 14.625 10.4788 14.625 11.375C14.625 12.2712 13.8962 13 13 13C12.1038 13 11.375 12.2712 11.375 11.375Z" fill="white"/>
<path d="M7.3125 16.25H18.6875V6.5H7.3125V16.25ZM13 8.125C14.5096 8.125 15.7714 9.165 16.1346 10.5625H17.0625C17.511 10.5625 17.875 10.9265 17.875 11.375C17.875 11.8235 17.511 12.1875 17.0625 12.1875H16.1346C15.7714 13.585 14.5096 14.625 13 14.625C11.2076 14.625 9.75 13.1674 9.75 11.375C9.75 9.58262 11.2076 8.125 13 8.125Z" fill="white"/>
<path d="M21.125 1.625H4.875C3.53112 1.625 2.4375 2.71862 2.4375 4.0625V18.6875C2.4375 20.0314 3.53112 21.125 4.875 21.125H21.125C22.4689 21.125 23.5625 20.0314 23.5625 18.6875V4.0625C23.5625 2.71862 22.4689 1.625 21.125 1.625ZM20.3125 17.0625C20.3125 17.511 19.9485 17.875 19.5 17.875H6.5C6.0515 17.875 5.6875 17.511 5.6875 17.0625V5.6875C5.6875 5.239 6.0515 4.875 6.5 4.875H19.5C19.9485 4.875 20.3125 5.239 20.3125 5.6875V17.0625Z" fill="white"/>
<path d="M17.0625 22.75H21.125V23.5625C21.125 24.011 20.761 24.375 20.3125 24.375H17.875C17.4265 24.375 17.0625 24.011 17.0625 23.5625V22.75Z" fill="white"/>
<path d="M4.875 22.75H8.9375V23.5625C8.9375 24.011 8.5735 24.375 8.125 24.375H5.6875C5.239 24.375 4.875 24.011 4.875 23.5625V22.75Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,108 @@
<template>
<div
class="flex-shrink-0 bg-white rounded-lg relative flex flex-col flex-1 px-6 pt-4 pb-6 dark:bg-dark-500"
style="box-shadow: -1px -3px 10px rgba(12, 25, 91, 0.03), 2px 4px 12px rgba(12, 25, 91, 0.05)"
>
<div class="flex items-center h-14">
<div class="flex mr-4 -space-x-3 overflow-hidden">
<div
v-if="tokenIcon"
class="inline-flex items-center justify-center dark:opacity-90 w-12 h-12"
>
<img class="w-full h-full object-cover" :src="tokenIcon" />
</div>
<IconCurrency v-else :currency="tokenKey" class="w-12 h-12" no-height />
</div>
<div class="flex flex-col flex-grow">
<div class="mb-1 font-medium leading-none whitespace-no-wrap text-19">
{{ formatUsd(supplyUsd) }}
</div>
<div class="flex leading-none whitespace-no-wrap">
<span class="text-grey-pure text-14"
>{{ formatDecimal(supply) }} {{ symbol }}</span
>
<Info
:text="`${formatUsd(priceInUsd, 2)}/${symbol}`"
icon="price"
class="ml-1"
/>
</div>
</div>
<div class="ml-auto text-right">
<p class="text-lg font-medium">{{ formatPercent(netAPY) }}</p>
<p class="text-sm font-medium text-[#9FB0C9]">net APY</p>
</div>
</div>
<hr class="mt-4" />
<div class="flex items-center justify-around mt-6">
<button
class="mr-4 h-10 w-full bg-primary-blue-dark shadow text-white rounded-[4px] hover:bg-primary-blue-hover"
@click="showSupply"
>
Supply
</button>
<button
class="h-10 w-full text-primary-blue-dark shadow border border-primary-blue-dark hover:border-primary-blue-hover rounded-[4px] hover:text-primary-blue-hover"
@click="showWithdraw"
>
Withdraw
</button>
</div>
</div>
</template>
<script>
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
import { useFormatting } from "~/composables/useFormatting";
import { useToken } from "~/composables/useToken";
export default defineComponent({
props: {
tokenKey: { type: String, required: true },
tokenIcon: { type: String, required: false },
vault: { type: String, default: null },
supply: { type: String, required: true },
supplyUsd: { type: String, required: true },
priceInUsd: { type: String, default: "0" },
netAPY: { type: String, default: "0" }
},
setup(props) {
const { app } = useContext();
const { formatPercent, formatUsd, formatDecimal } = useFormatting();
const { getTokenByKey } = useToken();
const symbol = computed(
() => getTokenByKey(props.tokenKey)?.symbol || props.tokenKey
);
function showSupply() {
app.router.push({ hash: `supply?vault=${props.vault}` });
}
function showWithdraw() {
app.router.push({ hash: `withdraw?vault=${props.vault}` });
}
return {
showSupply,
showWithdraw,
formatPercent,
formatUsd,
formatDecimal,
symbol
};
}
});
</script>
<style scoped>
.position-button {
@apply flex-1;
@apply h-8;
@apply h-8;
}
</style>

View File

@ -2,9 +2,9 @@
<SidebarContextContainer class="flex-1 h-full overflow-hidden">
<SidebarContextHeader><slot name="title" /></SidebarContextHeader>
<div class="overflow-y-scroll scrollbar-hover">
<div class="mx-auto w-full">
<div class="py-2 sm:py-4">
<div class="overflow-y-scroll scrollbar-hover h-full">
<div class="mx-auto w-full h-full">
<div class="py-2 sm:py-4 h-full">
<slot />
</div>
</div>

View File

@ -35,7 +35,7 @@
<SidebarSectionStatus
class="mt-8"
:liquidation="maxLiquidation"
:liquidation="liquidation"
:status="status"
/>
@ -212,6 +212,7 @@ export default defineComponent({
formatUsdMax,
formatUsd,
maxLiquidation,
liquidation,
liquidationPrice,
liquidationMaxPrice,
errorMessages,

View File

@ -0,0 +1,183 @@
<template>
<SidebarContextRootContainer>
<template #title>Supply {{ symbol }}</template>
<SidebarSectionValueWithIcon label="Token Balance" center>
<template #icon
><IconCurrency :currency="token.key" class="w-20 h-20" noHeight
/></template>
<template #value>{{ formatNumber(balance) }} {{ symbol }}</template>
</SidebarSectionValueWithIcon>
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8 h-full">
<h3 class="text-primary-gray text-xs font-semibold mb-2.5">
Amount to supply
</h3>
<input-numeric
v-model="amount"
placeholder="Amount to supply"
:error="errors.amount.message"
>
<template v-if="!isMaxAmount" #suffix>
<div class="absolute mt-2 top-0 right-0 mr-4">
<button
type="button"
class="text-primary-blue-dark font-semibold text-sm hover:text-primary-blue-hover"
@click="toggle"
>
Max
</button>
</div>
</template>
</input-numeric>
<div class="flex flex-shrink-0 mt-10">
<ButtonCTA
class="w-full"
:disabled="!isValid || pending"
:loading="pending"
@click="cast"
>
Supply
</ButtonCTA>
</div>
<ValidationErrors :error-messages="errorMessages" class="mt-6" />
</div>
</SidebarContextRootContainer>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from "@nuxtjs/composition-api";
import InputNumeric from "~/components/common/input/InputNumeric.vue";
import { useBalances } from "~/composables/useBalances";
import { useNotification } from "~/composables/useNotification";
import { useBigNumber } from "~/composables/useBigNumber";
import { useFormatting } from "~/composables/useFormatting";
import { useValidators } from "~/composables/useValidators";
import { useValidation } from "~/composables/useValidation";
import { useToken } from "~/composables/useToken";
import { useParsing } from "~/composables/useParsing";
import { useMaxAmountActive } from "~/composables/useMaxAmountActive";
import { useWeb3 } from "@instadapp/vue-web3";
import ToggleButton from "~/components/common/input/ToggleButton.vue";
import { useDSA } from "~/composables/useDSA";
import ButtonCTA from "~/components/common/input/ButtonCTA.vue";
import Button from "~/components/Button.vue";
import { useSidebar } from "~/composables/useSidebar";
import DSA from "dsa-connect";
import { useYearnV2Position } from "~/composables/protocols/useYearnV2Position";
export default defineComponent({
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
props: {
vault: { type: String, required: true }
},
setup(props) {
const { close } = useSidebar();
const { account } = useWeb3();
const { dsa } = useDSA();
const { getTokenByKey, valInt } = useToken();
const { getBalanceByKey, fetchBalances } = useBalances();
const { formatNumber, formatUsdMax, formatUsd } = useFormatting();
const { isZero, gt, plus } = useBigNumber();
const { parseSafeFloat } = useParsing();
const {
showPendingTransaction,
showWarning,
showConfirmedTransaction
} = useNotification();
const { vaults, refreshPosition } = useYearnV2Position();
const selectedVault = computed(() =>
vaults.value.find(v => v.address === props.vault)
);
const amount = ref("");
const amountParsed = computed(() => parseSafeFloat(amount.value));
const token = computed(() =>
selectedVault.value
? getTokenByKey(selectedVault.value.token.display_name.toLowerCase())
: null
);
const symbol = computed(() => token.value?.symbol);
const decimals = computed(() => token.value?.decimals);
const balance = computed(() => getBalanceByKey(token.value?.key));
const { toggle, isMaxAmount } = useMaxAmountActive(amount, balance);
const { validateAmount, validateIsLoggedIn } = useValidators();
const errors = computed(() => {
const hasAmountValue = !isZero(amount.value);
return {
amount: {
message: validateAmount(amountParsed.value, balance.value),
show: hasAmountValue
},
auth: { message: validateIsLoggedIn(!!account.value), show: true }
};
});
const { errorMessages, isValid } = useValidation(errors);
const pending = ref(false);
async function cast() {
pending.value = true;
const amount = isMaxAmount.value
? dsa.value.maxValue
: valInt(amountParsed.value, decimals.value);
const spells = dsa.value.Spell();
spells.add({
//@ts-ignore
connector: "YEARN-VAULT-A",
method: "deposit",
args: [props.vault, amount, 0, 0]
});
try {
const txHash = await (dsa.value as DSA).cast({
spells,
from: account.value,
onReceipt: async receipt => {
showConfirmedTransaction(receipt.transactionHash);
await fetchBalances(true);
await refreshPosition();
}
});
showPendingTransaction(txHash);
} catch (error) {
showWarning(error.message);
}
pending.value = false;
close();
}
return {
selectedVault,
pending,
cast,
errors,
amount,
token,
symbol,
balance,
formatNumber,
formatUsdMax,
formatUsd,
toggle,
isMaxAmount,
errorMessages,
isValid
};
}
});
</script>

View File

@ -0,0 +1,179 @@
<template>
<SidebarContextRootContainer>
<template #title>Withdraw {{ symbol }}</template>
<SidebarSectionValueWithIcon label="Token Balance" center>
<template #icon
><IconCurrency :currency="token.key" class="w-20 h-20" noHeight
/></template>
<template #value>{{ formatNumber(balance) }} {{ symbol }}</template>
</SidebarSectionValueWithIcon>
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8 h-full">
<h3 class="text-primary-gray text-xs font-semibold mb-2.5">
Amount to withdraw
</h3>
<input-numeric
v-model="amount"
placeholder="Amount to withdraw"
:error="errors.amount.message"
>
<template v-if="!isMaxAmount" #suffix>
<div class="absolute mt-2 top-0 right-0 mr-4">
<button
type="button"
class="text-primary-blue-dark font-semibold text-sm hover:text-primary-blue-hover"
@click="toggle"
>
Max
</button>
</div>
</template>
</input-numeric>
<div class="flex flex-shrink-0 mt-10">
<ButtonCTA
class="w-full"
:disabled="!isValid || pending"
:loading="pending"
@click="cast"
>
Withdraw
</ButtonCTA>
</div>
<ValidationErrors :error-messages="errorMessages" class="mt-6" />
</div>
</SidebarContextRootContainer>
</template>
<script>
import { computed, defineComponent, onMounted, ref } from '@nuxtjs/composition-api'
import InputNumeric from '~/components/common/input/InputNumeric.vue'
import { useBalances } from '~/composables/useBalances'
import { useBigNumber } from '~/composables/useBigNumber'
import { useFormatting } from '~/composables/useFormatting'
import { useValidators } from '~/composables/useValidators'
import { useValidation } from '~/composables/useValidation'
import { useToken } from '~/composables/useToken'
import { useParsing } from '~/composables/useParsing'
import { useMaxAmountActive } from '~/composables/useMaxAmountActive'
import { useWeb3 } from '@instadapp/vue-web3'
import ToggleButton from '~/components/common/input/ToggleButton.vue'
import { useDSA } from '~/composables/useDSA'
import ButtonCTA from '~/components/common/input/ButtonCTA.vue'
import { useNotification } from '~/composables/useNotification'
import Button from '~/components/Button.vue'
import { useSidebar } from '~/composables/useSidebar'
import { useYearnV2Position } from '~/composables/protocols/useYearnV2Position'
export default defineComponent({
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
props: {
vault: { type: String, required: true }
},
setup(props) {
const { close } = useSidebar()
const { account } = useWeb3()
const { dsa } = useDSA()
const { getTokenByKey, valInt } = useToken()
const { formatNumber, formatUsdMax, formatUsd } = useFormatting()
const { isZero, gt, plus, max, minus } = useBigNumber()
const { parseSafeFloat } = useParsing()
const { showPendingTransaction, showConfirmedTransaction, showWarning } = useNotification()
const { fetchBalances } = useBalances();
const { vaults, refreshPosition } = useYearnV2Position();
const selectedVault = computed(() =>
vaults.value.find(v => v.address === props.vault)
);
const balance = computed(
() => selectedVault.value ? selectedVault.value.position.supply : '0'
)
const amount = ref('')
const amountParsed = computed(() => parseSafeFloat(amount.value))
const token = computed(() =>
selectedVault.value
? getTokenByKey(selectedVault.value.token.display_name.toLowerCase())
: null
);
const symbol = computed(() => token.value?.symbol)
const decimals = computed(() => token.value?.decimals)
const { toggle, isMaxAmount } = useMaxAmountActive(amount, balance)
const { validateAmount, validateIsLoggedIn } = useValidators()
const errors = computed(() => {
const hasAmountValue = !isZero(amount.value)
return {
amount: { message: validateAmount(amountParsed.value, balance.value), show: hasAmountValue },
auth: { message: validateIsLoggedIn(!!account.value), show: true },
}
})
const { errorMessages, isValid } = useValidation(errors)
const pending = ref(false)
async function cast() {
pending.value = true
const amount = isMaxAmount.value ? dsa.value.maxValue : valInt(amountParsed.value, decimals.value)
const spells = dsa.value.Spell()
spells.add({
//@ts-ignore
connector: "YEARN-VAULT-A",
method: 'withdraw',
args: [props.vault, amount, 0, 0],
})
try {
const txHash = await dsa.value.cast({
spells,
from: account.value,
onReceipt: async receipt => {
showConfirmedTransaction(receipt.transactionHash);
await fetchBalances(true);
await refreshPosition();
}
})
showPendingTransaction(txHash)
} catch (error) {
showWarning(error.message)
}
pending.value = false
close()
}
return {
pending,
cast,
errors,
amount,
token,
symbol,
balance,
formatNumber,
formatUsdMax,
formatUsd,
toggle,
isMaxAmount,
errorMessages,
isValid
}
},
})
</script>

View File

@ -0,0 +1,138 @@
import { useWeb3 } from "@instadapp/vue-web3";
import { ref, useContext, watch } from "@nuxtjs/composition-api";
import addresses from "~/constant/addresses";
import tokens from "~/constant/tokens";
import { useDSA } from "../useDSA";
import yearnV2ABI from "~/abis/read/yearnV2.json";
import useEventBus from "../useEventBus";
import BigNumber from "bignumber.js";
import { useBigNumber } from "../useBigNumber";
const resolver = addresses.mainnet.resolver.yearnV2;
const allTokens = tokens.mainnet.allTokens.map(token => token.address);
const wantAddresses = allTokens;
const vaults = ref([]);
export function useYearnV2Position() {
const { $axios } = useContext();
const { times } = useBigNumber();
const { library } = useWeb3();
const { activeAccount } = useDSA();
const { onEvent } = useEventBus();
const fetchPosition = async () => {
const availableVaults = await $axios
.$get("https://api.yearn.finance/v1/chains/1/vaults/all")
.then(vs => vs.filter(v => v.type === "v2"));
if (!library.value) {
return;
}
if (!activeAccount.value) {
return;
}
const resolverInstance = new library.value.eth.Contract(
yearnV2ABI as any,
resolver
);
// const tokensArr = wantAddresses;
// allow user to add custom tokens
const tokensArr = [
...new Set(
availableVaults.filter(v => v.type === "v2").map(v => v.token.address)
.filter(a => allTokens.includes(a))
)
];
const rawData = await resolverInstance.methods
.getPositionsForLatest(activeAccount.value.address, tokensArr)
.call();
const newVaults = [];
rawData.forEach(
([
vaultLatestVersion,
vault,
want,
pricePerShare,
availableDepositLimit,
totalAssets,
balanceOf,
wantBalanceOf,
expectedShareValue,
decimals,
isDeprecated,
emergencyShutdown
]) => {
const v = availableVaults.find(v => v.address === vault);
if (v) {
const supply = new BigNumber(balanceOf)
.dividedBy(10 ** decimals)
.toFixed();
newVaults.push({
...v,
priceInUsd: v.tvl.price.toString(),
position: {
vaultLatestVersion,
vault,
want,
pricePerShare,
availableDepositLimit,
totalAssets,
balanceOf,
wantBalanceOf,
expectedShareValue,
decimals,
isDeprecated,
emergencyShutdown,
supply,
supplyUsd: times(supply, v.tvl.price).toFixed()
}
});
}
}
);
vaults.value = newVaults;
};
const refreshPosition = async () => {
await fetchPosition();
};
onEvent("protocol::compound::refresh", refreshPosition);
watch(
library,
async val => {
if (val) {
refreshPosition();
}
},
{ immediate: true }
);
watch(
activeAccount,
async val => {
if (val) {
refreshPosition();
}
},
{ immediate: true }
);
return {
vaults,
refreshPosition
};
}

View File

@ -43,6 +43,9 @@ import SidebarReflexerWithdraw from '~/components/sidebar/context/reflexer/Sideb
import SidebarReflexerBorrow from '~/components/sidebar/context/reflexer/SidebarReflexerBorrow.vue'
import SidebarReflexerPayback from '~/components/sidebar/context/reflexer/SidebarReflexerPayback.vue'
import SidebarYearnV2Supply from "~/components/sidebar/context/yearn-v2/SidebarYearnV2Supply.vue";
import SidebarYearnV2Withdraw from '~/components/sidebar/context/yearn-v2/SidebarYearnV2Withdraw.vue'
import SidebarStrategySelection from '~/components/sidebar/context/strategy/SidebarStrategySelection.vue'
import SidebarStrategy from '~/components/sidebar/context/strategy/SidebarStrategy.vue'
@ -86,6 +89,11 @@ const sidebars = {
"/mainnet/reflexer#withdraw": { component: SidebarReflexerWithdraw },
"/mainnet/reflexer#borrow": { component: SidebarReflexerBorrow },
"/mainnet/reflexer#payback": { component: SidebarReflexerPayback },
"/mainnet/yearn-v2": { component: null },
"/mainnet/yearn-v2#supply": { component: SidebarYearnV2Supply },
"/mainnet/yearn-v2#withdraw": { component: SidebarYearnV2Withdraw },
};
const sidebar = ref(null);

View File

@ -10,7 +10,8 @@ const addresses = {
maker: "0x84addce4fac0b6ee4b0cd132120d6d4b700e35c0",
unipool: "0x22bddA39D14eD0aafeee36B6e784602fdDE64723",
liquity: "0xDAf2A39503463B0F41f899EDD82213b3c96b6Cf8",
reflexer: "0x016ca8d0993d1a7073b01802a2e22fd0df7e633a"
reflexer: "0x016ca8d0993d1a7073b01802a2e22fd0df7e633a",
yearnV2: "0x3f6DCA8a0b7d04737BC3B2aEAbeB1C09431581f0"
}
},

View File

@ -9,7 +9,17 @@
class="px-8 md:px-4 max-w-6xl mx-auto"
:class="{ 'text-center': !active, 'py-12': active }"
>
<Nuxt v-if="active" />
<div v-if="active && activeAccount && activeAccount.version === '1'" class="text-center w-full my-16">
<h3 class="font-semibold text-2xl">
Assembly doesn't support DSA v1
</h3>
<p
class="mt-4 font-medium leading-normal text-grey-pure text-base"
>
Please create or switch to DSA v2.
</p>
</div>
<Nuxt v-else-if="active" />
<web-3-modal slim v-else />
</div>
@ -95,6 +105,7 @@ import { useNetwork } from "~/composables/useNetwork";
import { useModal } from "~/composables/useModal";
import { useEagerConnect } from "~/composables/useEagerConnect";
import Web3Modal from "~/components/modal/web3/Web3Modal.vue";
import { useDSA } from "~/composables/useDSA";
export default defineComponent({
components: {
@ -105,6 +116,7 @@ export default defineComponent({
},
setup() {
const { active, activate, deactivate, chainId } = useWeb3();
const { activeAccount } = useDSA();
const { activeNetworkId, activeNetwork, checkForNetworkMismatch } = useNetwork();
const { isShown: isBackdropShown, close: closeBackdrop } = useBackdrop()
const { redirect } = useContext()
@ -154,6 +166,7 @@ export default defineComponent({
})
return {
activeAccount,
active,
activate,
deactivate,

View File

@ -30,7 +30,7 @@
"bignumber.js": "^9.0.1",
"core-js": "^3.15.1",
"css-color-function": "^1.3.3",
"dsa-connect": "^0.4.9",
"dsa-connect": "^0.5.0",
"nuxt": "^2.15.7",
"qrcode": "^1.4.4",
"slugify": "^1.6.0",

View File

@ -131,7 +131,7 @@
<div class="ml-2"><Info text="Debt/Collateral ratio" /></div>
</div>
<span>Max - {{ formatPercent(position.maxLiquidation) }}</span>
<span>Max - {{ formatPercent(maxLiquidation) }}</span>
</div>
</div>
</div>
@ -207,7 +207,8 @@ export default defineComponent({
totalSupply,
totalBorrow,
status,
liquidation
liquidation,
maxLiquidation,
} = useAaveV2Position();
const { div } = useBigNumber();
@ -236,7 +237,8 @@ export default defineComponent({
formatUsd,
formatPercent,
color,
text
text,
maxLiquidation,
};
}
});

View File

@ -38,6 +38,7 @@ import MakerIcon from "~/assets/icons/makerdao.svg?inline";
import OneInchIcon from "~/assets/icons/1inch.svg?inline";
import LiquityIcon from "~/assets/icons/liquity.svg?inline";
import ReflexerIcon from "~/assets/icons/reflexer.svg?inline";
import YearnIcon from "~/assets/icons/yearn.svg?inline";
const appsPerNetwork = {
mainnet: [
@ -82,6 +83,13 @@ const appsPerNetwork = {
name: "Reflexer Finance",
url: "/mainnet/reflexer",
description: "Collateralized RAI Debt"
},
{
id: "yearn-v2",
icon: YearnIcon,
name: "Yearn",
url: "/mainnet/yearn-v2",
description: "Automated Yield Strategies"
}
],
polygon: [

View File

@ -131,7 +131,7 @@
<div class="ml-2"><Info text="Debt/Collateral ratio" /></div>
</div>
<span>Max - {{ formatPercent(position.maxLiquidation) }}</span>
<span>Max - {{ formatPercent(liquidation) }}</span>
</div>
</div>
</div>
@ -205,7 +205,7 @@ export default defineComponent({
totalSupply,
totalBorrow,
status,
liquidation
liquidation,
} = useCompoundPosition();
const { div } = useBigNumber();
@ -234,7 +234,8 @@ export default defineComponent({
formatUsd,
formatPercent,
color,
text
text,
liquidation,
};
}
});

100
pages/mainnet/yearn-v2.vue Normal file
View File

@ -0,0 +1,100 @@
<template>
<div>
<div>
<nuxt-link
to="/"
class="text-[#C0C5D7] text-lg font-semibold flex items-center"
>
<BackIcon class="w-4 h-4 mr-3" />
Apps
</nuxt-link>
</div>
<div class="mt-10 flex items-center justify-between">
<div class="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="w-12 h-12 rounded-full flex items-center justify-center bg-[#1874FF]"
>
<YearnIcon class="w-8 h-8 text-white" />
</div>
</div>
<h1 class="ml-4 text-primary-black text-2xl font-semibold">Yearn</h1>
</div>
</div>
<div class="mt-[60px]">
<div
class="w-full flex flex-col mt-6 sm:flex-row sm:items-center sm:justify-between xl:mt-4"
>
<h2 class="text-primary-gray text-lg font-semibold">Your Positions</h2>
<div class="mt-4 sm:mt-0 sm:mr-1">
<SearchInput
v-model.trim="search"
dense
class="w-[200px]"
placeholder="Search positions"
/>
</div>
</div>
<div
class="mt-3 grid w-full grid-cols-1 gap-4 sm:grid-cols-2 xxl:gap-6 min-w-max-content px-1"
>
<div v-for="item in filteredVaults" :key="item.symbol">
<card-yearn
:vault="item.address"
:token-key="item.token.display_name.toLowerCase()"
:supply="item.position.supply"
:supply-usd="item.position.supplyUsd"
:type="item.type"
:price-in-usd="item.priceInUsd"
:netAPY="item.apy.net_apy"
/>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from "@nuxtjs/composition-api";
import BackIcon from "~/assets/icons/back.svg?inline";
import { useYearnV2Position } from "~/composables/protocols/useYearnV2Position";
import { useFormatting } from "~/composables/useFormatting";
import { useSearchFilter } from "~/composables/useSearchFilter";
import CardYearn from "~/components/protocols/yearn-v2/CardYearn.vue";
import YearnIcon from "~/assets/icons/yearn.svg?inline";
import ButtonCTAOutlined from "~/components/common/input/ButtonCTAOutlined.vue";
export default defineComponent({
components: {
BackIcon,
CardYearn,
YearnIcon,
ButtonCTAOutlined,
},
setup() {
const { vaults } = useYearnV2Position();
const { formatUsd, formatPercent } = useFormatting();
const { filtered: filteredVaults, search } = useSearchFilter(
vaults,
"key",
"display_name"
);
return {
filteredVaults,
search,
formatUsd,
formatPercent
};
}
});
</script>

2401
yarn.lock

File diff suppressed because it is too large Load Diff