This commit is contained in:
shmuel 2021-10-10 14:24:44 +03:00
commit 95d148f570
28 changed files with 3036 additions and 721 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"
}
]

20
assets/icons/arbitrum.svg Normal file
View File

@ -0,0 +1,20 @@
<svg viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0arb)">
<path d="M10.5337 12.0149L12.2372 14.7045L13.8109 13.7871L11.5736 10.2397L10.5337 12.0149Z" fill="currentColor" />
<path
d="M15.2441 11.9721L12.7995 8.14176L11.8925 9.68992L14.2525 13.5294L15.1057 13.032C15.1895 12.9636 15.2401 12.8632 15.2454 12.755L15.2441 11.9721Z"
fill="currentColor" />
<path
d="M5 12.9604C5 13.2327 5.14504 13.4844 5.38065 13.621L6.20496 14.0988L10.2148 7.62987L9.53425 7.61181C8.9544 7.60359 8.32976 7.75522 8.0431 8.22599L5.76604 11.7763L5 12.9604Z"
fill="currentColor" />
<path d="M12.5291 7.62917L10.7355 7.63572L6.67766 14.3718L8.09601 15.1933L12.5291 7.62917Z" fill="currentColor" />
<path
d="M16 7.623C15.985 7.24569 15.7819 6.90026 15.4638 6.69913L11.0048 4.11963C10.6901 3.96024 10.297 3.96004 9.98178 4.11953C9.94451 4.13844 5.64557 6.6465 5.64557 6.6465C5.58608 6.67521 5.52878 6.70942 5.47486 6.74824C5.19089 6.95296 5.01778 7.27114 5 7.6207V12.9628L5.76604 11.7788L5.75937 7.6568C5.76027 7.64172 5.76212 7.62689 5.76481 7.61224C5.78213 7.51528 5.83596 7.42875 5.91701 7.37028C5.93723 7.35572 10.3096 4.80972 10.3235 4.80274C10.4263 4.75082 10.5569 4.7502 10.6598 4.80112L15.061 7.34782C15.1651 7.41455 15.2304 7.52766 15.2362 7.65129V12.7574C15.2309 12.8657 15.1883 12.966 15.1047 13.0344L10.6416 15.6363C10.5556 15.6675 10.4551 15.6657 10.3699 15.6309L8.48168 14.5384L8.09597 15.1965L9.79282 16.1793C9.84893 16.2114 9.8989 16.2398 9.93994 16.263C10.0035 16.2988 10.0468 16.3228 10.062 16.3303C10.1826 16.3892 10.3562 16.4235 10.5125 16.4235C10.6559 16.4235 10.7957 16.397 10.928 16.3449L15.5634 13.6444C15.8294 13.437 15.9859 13.1243 16 12.7855V7.623Z"
fill="currentColor" />
</g>
<defs>
<clipPath id="clip0arb">
<rect x="0.580627" width="20" height="20" rx="2" fill="currentColor" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

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

@ -3,7 +3,7 @@
<template #title>Supply to Stability Pool</template>
<div class="flex justify-around items-center w-full">
<SidebarSectionValueWithIcon class="" label="Debt" center>
<SidebarSectionValueWithIcon class="" label="Stability Pool Balance" center>
<template #icon
><IconCurrency :currency="poolToken.key" class="w-16 h-16" noHeight
/></template>

View File

@ -3,7 +3,7 @@
<template #title>Withdraw from Stability Pool</template>
<div class="flex justify-around items-center w-full">
<SidebarSectionValueWithIcon class="" label="Debt" center>
<SidebarSectionValueWithIcon class="" label="Stability Pool Balance" center>
<template #icon
><IconCurrency :currency="poolToken.key" class="w-16 h-16" noHeight
/></template>

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

@ -347,7 +347,7 @@
</template>
<script>
import { computed, defineComponent, reactive, ref, watch } from '@nuxtjs/composition-api'
import { computed, defineComponent, onMounted, reactive, ref, watch } from '@nuxtjs/composition-api'
import { useNetwork } from '~/composables/useNetwork'
import tokens from '~/constant/tokens'
import Button from '../Button.vue'
@ -404,6 +404,10 @@ export default defineComponent({
decimals: 18,
})
onMounted(() => {
selectToken1(allTokens.value[1])
})
const slippagePerc = computed(() => {
const sellAmountUsd = toBN(token0.amountUSD);

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

@ -22,24 +22,27 @@ import { useSorting } from "./useSorting";
const balances = reactive({
user: {
mainnet: {},
polygon: {}
polygon: {},
arbitrum: {}
},
dsa: {
mainnet: {},
polygon: {}
polygon: {},
arbitrum: {}
}
});
const prices = reactive({
mainnet: {},
polygon: {}
polygon: {},
arbitrum: {}
});
export function useBalances() {
const { $axios } = useContext();
const { times, plus, ensureValue } = useBigNumber();
const { account, library } = useWeb3();
const { activeNetworkId } = useNetwork()
const { activeNetworkId } = useNetwork();
const { activeAccount } = useDSA();
const { getTokenByKey } = useToken();
const { by } = useSorting();
@ -49,6 +52,9 @@ export function useBalances() {
prices.polygon = await $axios.$get(
"https://api.instadapp.io/defi/polygon/prices"
);
prices.arbitrum = await $axios.$get(
"https://api.instadapp.io/defi/arbitrum/prices"
);
});
const fetchBalances = async (refresh = false) => {
if (!balances.user || refresh) {
@ -61,6 +67,10 @@ export function useBalances() {
polygon:
activeNetworkId.value === Network.Polygon
? await getBalances(account.value, Network.Polygon, library.value)
: {},
arbitrum:
activeNetworkId.value === Network.Arbitrum
? await getBalances(account.value, Network.Arbitrum, library.value)
: {}
};
}
@ -84,6 +94,14 @@ export function useBalances() {
Network.Polygon,
library.value
)
: {},
arbitrum:
activeNetworkId.value === Network.Arbitrum
? await getBalances(
activeAccount.value.address,
Network.Arbitrum,
library.value
)
: {}
};
}
@ -95,7 +113,8 @@ export function useBalances() {
const getBalanceByAddress = (address, network = null, type = "dsa") => {
return (
balances[type]?.[network || activeNetworkId.value][address]?.balance || "0"
balances[type]?.[network || activeNetworkId.value][address]?.balance ||
"0"
);
};
@ -126,7 +145,11 @@ export function useBalances() {
return tokens[activeNetworkId.value].allTokens
.map(token => ({
...token,
balance: getBalanceByAddress(token.address, activeNetworkId.value, type),
balance: getBalanceByAddress(
token.address,
activeNetworkId.value,
type
),
netWorth: netWorth(token.address, type)
}))
.sort(by("-netWorth"));

View File

@ -8,6 +8,9 @@ export function useLink() {
if (activeNetworkId.value === "polygon") {
return "https://polygonscan.com/address";
}
if (activeNetworkId.value === "arbitrum") {
return "https://arbiscan.io/address";
}
return "https://etherscan.io/address";
});
@ -21,5 +24,7 @@ export const getMaticLink = transactionHash =>
`https://polygonscan.com/tx/${transactionHash}`;
export const getPolygonLink = transactionHash =>
`https://polygonscan.com/tx/${transactionHash}`;
export const getArbitrumLink = transactionHash =>
`https://arbiscan.io/tx/${transactionHash}`;
export const getTenderlyLink = simulationId =>
`https://dashboard.tenderly.co/public/InstaDApp/dsa-simulations/fork-simulation/${simulationId}?hideSidebar=true`;

View File

@ -2,6 +2,8 @@ import { computed, watchEffect, ref, watch } from "@nuxtjs/composition-api";
import MainnetSVG from "~/assets/icons/mainnet.svg?inline";
import PolygonSVG from "~/assets/icons/polygon.svg?inline";
import ArbitrumSVG from "~/assets/icons/arbitrum.svg?inline";
import { useModal } from "./useModal";
import { useNotification } from "./useNotification";
import { useWeb3 } from "@instadapp/vue-web3";
@ -9,12 +11,14 @@ import { useCookies } from "./useCookies";
export enum Network {
Mainnet = "mainnet",
Polygon = "polygon"
Polygon = "polygon",
Arbitrum = "arbitrum"
}
export const networks = [
{ id: "mainnet", chainId: 1, name: "Mainnet", icon: MainnetSVG },
{ id: "polygon", chainId: 137, name: "Polygon", icon: PolygonSVG }
{ id: "polygon", chainId: 137, name: "Polygon", icon: PolygonSVG },
{ id: "arbitrum", chainId: 42161, name: "Arbitrum", icon: ArbitrumSVG }
];
export const activeNetworkId = ref<Network>();
@ -93,10 +97,50 @@ export function useNetwork() {
}
}
async function switchToArbitrum() {
if (window.ethereum) {
const chainId = "0xa4b1";
try {
await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId }]
});
} catch (switchError) {
// 4902 error code indicates that the chain has not been added to MetaMask.
if (switchError.code === 4902) {
try {
const chainData = {
chainId,
chainName: 'Arbitrum One',
nativeCurrency: {
name: 'Ethereum',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://arb1.arbitrum.io/rpc'],
blockExplorerUrls: ['https://arbiscan.io'],
};
await window.ethereum.request({
method: "wallet_addEthereumChain",
params: [chainData, account.value]
});
} catch (addError) {
return Promise.reject(addError);
}
} else {
return Promise.reject(switchError);
}
}
}
}
async function switchNetwork() {
try {
if (activeNetworkId.value === "mainnet") {
await switchToMainnet();
} else if (activeNetworkId.value === "arbitrum") {
await switchToArbitrum();
} else {
await switchToPolygon();
}

View File

@ -1,6 +1,11 @@
import { ref } from "@nuxtjs/composition-api";
import { useFormatting } from "@/composables/useFormatting";
import { getEtherscanLink, getMaticLink, getTenderlyLink } from "./useLink";
import {
getArbitrumLink,
getEtherscanLink,
getMaticLink,
getTenderlyLink
} from "./useLink";
import { useRandom } from "./useRandom";
import { Network, activeNetworkId } from "./useNetwork";
const { makeid } = useRandom();
@ -92,6 +97,8 @@ export function useNotification() {
let href;
if (network === Network.Polygon) {
href = getMaticLink(transactionHash);
}else if (network === Network.Arbitrum) {
href = getArbitrumLink(transactionHash);
} else {
href = getEtherscanLink(transactionHash);
}
@ -113,6 +120,8 @@ export function useNotification() {
let href;
if (network === Network.Polygon) {
href = getMaticLink(transactionHash);
} else if (network === Network.Arbitrum) {
href = getArbitrumLink(transactionHash);
} else {
href = getEtherscanLink(transactionHash);
}

View File

@ -46,6 +46,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'
@ -92,6 +95,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

@ -9,9 +9,10 @@ const forkId = ref(null);
export function useTenderly() {
const { $config } = useContext();
const { activate, deactivate, connector, library } = useWeb3();
const { activeNetworkId } = useNetwork();
const { accounts, refreshAccounts } = useDSA();
const canSimulate = computed(
() => $config.TENDERLY_FORK_PATH && $config.TENDERLY_KEY
() => activeNetworkId.value !== "arbitrum" && $config.TENDERLY_FORK_PATH && $config.TENDERLY_KEY
);
const loading = ref(false);

View File

@ -14,15 +14,16 @@ import { SafeAppConnector } from "@gnosis.pm/safe-apps-web3-react/dist/connector
setWeb3LibraryCallback(provider => new Web3(provider));
export const injected = new InjectedConnector({
supportedChainIds: [1, 137]
supportedChainIds: [1, 137, 42161]
});
export const walletconnect = new WalletConnectConnector({
rpc: {
1: `https://mainnet.infura.io/v3/${process.env.INFURA_ID}`,
137: "https://rpc-mainnet.maticvigil.com"
137: "https://rpc-mainnet.maticvigil.com",
42161: "https://arb1.arbitrum.io/rpc"
},
supportedChainIds: [1, 137]
supportedChainIds: [1, 137, 42161]
});
// mainnet only
@ -41,7 +42,7 @@ let gnosisSafe = null;
if (process.client) {
gnosisSafe = new SafeAppConnector({
supportedChainIds: [1, 137]
supportedChainIds: [1, 137, 42161]
});
}

View File

@ -12,7 +12,8 @@ const addresses = {
unipool: "0x22bddA39D14eD0aafeee36B6e784602fdDE64723",
liquity: "0xDAf2A39503463B0F41f899EDD82213b3c96b6Cf8",
reflexer: "0x016ca8d0993d1a7073b01802a2e22fd0df7e633a",
bprotocol: "0x3843019c19259117ed473947007bcafc5c0c7129"
bprotocol: "0x3843019c19259117ed473947007bcafc5c0c7129",
yearnV2: "0x3f6DCA8a0b7d04737BC3B2aEAbeB1C09431581f0"
}
},
@ -30,7 +31,21 @@ const addresses = {
},
weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
}
},
arbitrum: {
core: {
instaIndex: '0x1eE00C305C51Ff3bE60162456A9B533C07cD9288',
instaConnectorsV2: '0x67fCE99Dd6d8d659eea2a1ac1b8881c57eb6592B'
},
resolver: {
accounts: '0xdF19Da523DA64bBE82eE0E4DFf00d676A8386474',
balance: '0x29572b16D306acd7ca0CBCA0F08C3EFF131fFDA5',
uniswap_v3: '0x04F8a41be023f839E709eeEaA0725FD766139A4d',
// uniswap_v3_staker: '0x1a96af80ED8753a77E23074De78480675049A3c9',
nonfungiblePositionManager: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
},
uniswapV3Staker: '0x1f98407aaB862CdDeF78Ed252D6f557aA5b0f00d',
},
};
export default addresses;

View File

@ -59,6 +59,15 @@ export default {
{ key: 'wmatic', type: 'token', symbol: 'WMATIC', name: 'Wrapped MATIC', address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', decimals: 18, isStableCoin: false},
{ key: 'wbtc', type: 'token', symbol: 'WBTC', name: 'Wrapped BTC', address: '0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6', decimals: 8, isStableCoin: false},
{ key: 'aave', type: 'token', symbol: 'AAVE', name: 'Aave Token', address: '0xD6DF932A45C0f255f85145f286eA0b292B21C90B', decimals: 18, isStableCoin: false },
])
]),
arbitrum: createTokenUtils([
{ key: 'eth', type: 'token', symbol: 'ETH', name: 'Ethereum', address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', decimals: 18, isStableCoin: false },
{ key: 'weth', type: 'token', symbol: 'WETH', name: 'Wrapped Ethereum', address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', decimals: 18, isStableCoin: false },
{ key: 'usdc', type: 'token', symbol: 'USDC', name: 'USD Coin', address: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', decimals: 6, isStableCoin: true },
{ key: 'usdt', type: 'token', symbol: 'USDT', name: 'Tether USD Coin', address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', decimals: 6, isStableCoin: true },
{ key: 'link', type: 'token', symbol: 'LINK', name: 'ChainLink Token', address: '0xf97f4df75117a78c1A5a0DBb814Af92458539FB4', decimals: 18, isStableCoin: false },
{ key: 'uni', type: 'token', symbol: 'UNI', name: 'Uniswap Token', address: '0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0', decimals: 18, isStableCoin: false },
{ key: 'wbtc', type: 'token', symbol: 'WBTC', name: 'Wrapped BTC', address: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', decimals: 8, isStableCoin: false },
])
}

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()
@ -133,14 +145,14 @@ export default defineComponent({
return;
}
if (route.value.path.includes(['mainnet', 'polygon']) && route.value.path.includes(activeNetwork.value.id)) {
if (route.value.path.includes(['mainnet', 'polygon', "arbitrum"]) && route.value.path.includes(activeNetwork.value.id)) {
redirect('/')
}
}, { immediate: true })
watch(chainId, (val) => {
if (val) {
if ([1, 137].includes(val)) {
if ([1, 137, 42161].includes(val)) {
checkForNetworkMismatch()
} else {
showNetworksMismatchDialog();
@ -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

@ -39,6 +39,7 @@ import OneInchIcon from "~/assets/icons/1inch.svg?inline";
import LiquityIcon from "~/assets/icons/liquity.svg?inline";
import BprotocolIcon from "~/assets/icons/b-protocol.svg?inline";
import ReflexerIcon from "~/assets/icons/reflexer.svg?inline";
import YearnIcon from "~/assets/icons/yearn.svg?inline";
const appsPerNetwork = {
mainnet: [
@ -90,6 +91,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: [
@ -107,6 +115,15 @@ const appsPerNetwork = {
url: "/1inch",
description: "DEX Aggregator"
}
],
arbitrum: [
{
id: "1inch",
icon: OneInchIcon,
name: "1inch",
url: "/1inch",
description: "DEX Aggregator"
}
]
};

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