From 33a44112019f911a01fd25a616511169d9efcc20 Mon Sep 17 00:00:00 2001 From: UniverseFinance Date: Fri, 3 Dec 2021 16:45:02 +0800 Subject: [PATCH 1/4] add universe finance --- abis/read/universe.json | 189 ++++++++++++++++++ assets/icons/universe.svg | 21 ++ components/protocols/CardUniverse.vue | 156 +++++++++++++++ .../universe/SidebarUniverseSupply.vue | 186 +++++++++++++++++ .../universe/SidebarUniverseWithdraw.vue | 181 +++++++++++++++++ composables/protocols/useUniverseOverview.ts | 31 +++ composables/protocols/useUniversePosition.ts | 153 ++++++++++++++ composables/useSidebar.ts | 7 + constant/addresses.ts | 3 +- pages/index.vue | 10 +- pages/mainnet/universe.vue | 167 ++++++++++++++++ 11 files changed, 1102 insertions(+), 2 deletions(-) create mode 100644 abis/read/universe.json create mode 100644 assets/icons/universe.svg create mode 100644 components/protocols/CardUniverse.vue create mode 100644 components/sidebar/context/universe/SidebarUniverseSupply.vue create mode 100644 components/sidebar/context/universe/SidebarUniverseWithdraw.vue create mode 100644 composables/protocols/useUniverseOverview.ts create mode 100644 composables/protocols/useUniversePosition.ts create mode 100644 pages/mainnet/universe.vue diff --git a/abis/read/universe.json b/abis/read/universe.json new file mode 100644 index 0000000..8aeb92c --- /dev/null +++ b/abis/read/universe.json @@ -0,0 +1,189 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "universeVault", + "type": "address" + } + ], + "name": "getVaultDetail", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxShare0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxShare1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSingeDepositAmt0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSingeDepositAmt1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "total0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "total1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "utilizationRate0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "utilizationRate1", + "type": "uint256" + } + ], + "internalType": "struct Helpers.VaultData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "universeVault", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserShareAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "universeVaults", + "type": "address[]" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserShareAmountList", + "outputs": [ + { + "internalType": "uint256[2][]", + "name": "data", + "type": "uint256[2][]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "universeVault", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserWithdrawAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "universeVault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "getUserDepositAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/assets/icons/universe.svg b/assets/icons/universe.svg new file mode 100644 index 0000000..eb39f72 --- /dev/null +++ b/assets/icons/universe.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/protocols/CardUniverse.vue b/components/protocols/CardUniverse.vue new file mode 100644 index 0000000..7f2c9d8 --- /dev/null +++ b/components/protocols/CardUniverse.vue @@ -0,0 +1,156 @@ + + + + diff --git a/components/sidebar/context/universe/SidebarUniverseSupply.vue b/components/sidebar/context/universe/SidebarUniverseSupply.vue new file mode 100644 index 0000000..aaed400 --- /dev/null +++ b/components/sidebar/context/universe/SidebarUniverseSupply.vue @@ -0,0 +1,186 @@ + + + diff --git a/components/sidebar/context/universe/SidebarUniverseWithdraw.vue b/components/sidebar/context/universe/SidebarUniverseWithdraw.vue new file mode 100644 index 0000000..5a14f97 --- /dev/null +++ b/components/sidebar/context/universe/SidebarUniverseWithdraw.vue @@ -0,0 +1,181 @@ + + + diff --git a/composables/protocols/useUniverseOverview.ts b/composables/protocols/useUniverseOverview.ts new file mode 100644 index 0000000..26bab20 --- /dev/null +++ b/composables/protocols/useUniverseOverview.ts @@ -0,0 +1,31 @@ +import { onMounted, ref, useContext } from '@nuxtjs/composition-api'; + +const overview = ref({ + tvl: 0, totalProfits: 0, totalGasSaved: 0 +}); + +type OverviewModel = { + tvl: number + totalProfits: number + totalGasSaved: number +} + +export function useUniverseOverview() { + const { $axios } = useContext(); + + const fetchOverview = async () => { + const { data } = await $axios + .$get<{ data: OverviewModel }>("https://defi-test-api.webwxk.com/singleVault/universe/instadapp/overview") + + + overview.value = data; + }; + + onMounted(() => { + fetchOverview(); + }); + + return { + overview + }; +} diff --git a/composables/protocols/useUniversePosition.ts b/composables/protocols/useUniversePosition.ts new file mode 100644 index 0000000..00a0beb --- /dev/null +++ b/composables/protocols/useUniversePosition.ts @@ -0,0 +1,153 @@ +import BigNumber from 'bignumber.js'; +import { useWeb3 } from '@instadapp/vue-web3'; +import { ref, useContext, watch } from '@nuxtjs/composition-api'; + +import universeABI from '~/abis/read/universe.json'; +import addresses from '~/constant/addresses'; +import tokens from '~/constant/tokens'; + +import { useBigNumber } from '../useBigNumber'; +import { useDSA } from '../useDSA'; +import useEventBus from '../useEventBus'; + +const resolver = addresses.mainnet.resolver.universe; + +const allTokens = tokens.mainnet.allTokens.map(token => token.address); + +const vaults = ref([]); +const totalDeposit = ref(0); +const totalUNTReward = ref(0); + +interface VaultModel { + tokenSymbol: string + tokenAddress: string + tokenDecimals: number + tokenIndex: 0 | 1 + vaultName: string + vaultAddress: string + feeAprLifetime: string + feeApr24h: string + netReturn: string + netApr: string + price: string + untReward: number + totalUnclaimedUnt: number +} + +type Vault = VaultModel & { + deposit: string, + depositInUsd: string + link: string +} + +export function useUniversePosition() { + const { $axios } = useContext(); + const { times } = useBigNumber(); + const { library } = useWeb3(); + const { activeAccount } = useDSA(); + const { onEvent } = useEventBus(); + + const fetchPosition = async () => { + const { data: availableVaults } = await $axios + .$get<{ data: VaultModel[] }>("https://defi-test-api.webwxk.com/singleVault/universe/instadapp/vaultList") + + if (!library.value) { + return; + } + + if (!activeAccount.value) { + vaults.value = availableVaults.map(vault => { + return { + ...vault, + link: `https://universe.finance/single/vault/${vault.vaultAddress}?watch=`, + deposit: '0', + depositInUsd: '0' + } + }); + + return; + } + + const resolverInstance = new library.value.eth.Contract( + universeABI as any, + resolver + ); + + const vaultAddressArr = [ + ...new Set(availableVaults.map(v => v.vaultAddress)) + ] + + const account = activeAccount.value.address; + const rawData = await resolverInstance.methods + .getUserShareAmountList(vaultAddressArr, account) + .call(); + const vaultsAmounts = vaultAddressArr.map((address, index) => ({ + vaultAddress: address, + token0Amount: rawData[index][0], + token1Amount: rawData[index][1] + })) + let totalDepositInUsd = 0; + let untReward = 0; + + const newVaults = []; + + availableVaults.forEach(vault => { + const amounts = vaultsAmounts.find(vm => vm.vaultAddress === vault.vaultAddress); + + if (amounts) { + const amount = vault.tokenIndex === 0 ? amounts.token0Amount : amounts.token1Amount; + const deposit = new BigNumber(amount) + .dividedBy(10 ** vault.tokenDecimals) + .toFixed(); + const depositInUsd = times(deposit, vault.price === '0' ? 1 : vault.price).toFixed() + totalDepositInUsd += Number(depositInUsd); + untReward += vault.totalUnclaimedUnt; + + const item = { + ...vault, + link: `https://universe.finance/single/vault/${vault.vaultAddress}?watch=${account}`, + deposit, + depositInUsd + } + newVaults.push(item); + } + }) + + vaults.value = newVaults; + totalDeposit.value = totalDepositInUsd; + totalUNTReward.value = untReward; + }; + + const refreshPosition = async () => { + await fetchPosition(); + }; + + onEvent("protocol::universe::refresh", refreshPosition); + + watch( + library, + async val => { + if (val) { + refreshPosition(); + } + }, + { immediate: true } + ); + + watch( + activeAccount, + async val => { + if (val) { + refreshPosition(); + } + }, + { immediate: true } + ); + + return { + vaults, + totalDeposit, + totalUNTReward, + refreshPosition, + }; +} diff --git a/composables/useSidebar.ts b/composables/useSidebar.ts index 89d5668..408ebe5 100644 --- a/composables/useSidebar.ts +++ b/composables/useSidebar.ts @@ -49,6 +49,9 @@ import SidebarReflexerPayback from '~/components/sidebar/context/reflexer/Sideba import SidebarYearnV2Supply from "~/components/sidebar/context/yearn-v2/SidebarYearnV2Supply.vue"; import SidebarYearnV2Withdraw from '~/components/sidebar/context/yearn-v2/SidebarYearnV2Withdraw.vue' +import SidebarUniverseSupply from "~/components/sidebar/context/universe/SidebarUniverseSupply.vue"; +import SidebarUniverseWithdraw from '~/components/sidebar/context/universe/SidebarUniverseWithdraw.vue' + import SidebarStrategySelection from '~/components/sidebar/context/strategy/SidebarStrategySelection.vue' import SidebarStrategy from '~/components/sidebar/context/strategy/SidebarStrategy.vue' @@ -71,6 +74,10 @@ const sidebars = { "/mainnet/compound#borrow": { component: SidebarCompoundBorrow }, "/mainnet/compound#payback": { component: SidebarCompoundPayback }, + "/mainnet/universe": { component: null }, + "/mainnet/universe#supply": { component: SidebarUniverseSupply }, + "/mainnet/universe#withdraw": { component: SidebarUniverseWithdraw }, + "/mainnet/maker": { component: null }, '/mainnet/maker#collateral': { component: SidebarMakerdaoCollateral }, "/mainnet/maker#supply": { component: SidebarMakerdaoSupply }, diff --git a/constant/addresses.ts b/constant/addresses.ts index baf73db..7fcc5cf 100644 --- a/constant/addresses.ts +++ b/constant/addresses.ts @@ -13,7 +13,8 @@ const addresses = { liquity: "0xDAf2A39503463B0F41f899EDD82213b3c96b6Cf8", reflexer: "0x016ca8d0993d1a7073b01802a2e22fd0df7e633a", bprotocol: "0x3843019c19259117ed473947007bcafc5c0c7129", - yearnV2: "0x3f6DCA8a0b7d04737BC3B2aEAbeB1C09431581f0" + yearnV2: "0x3f6DCA8a0b7d04737BC3B2aEAbeB1C09431581f0", + universe: "0xa7963898453c00b61cff8ce7e5b28c4e8bf1348f" } }, diff --git a/pages/index.vue b/pages/index.vue index f40d3ac..4d20d22 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -40,6 +40,7 @@ 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"; +import UniverseIcon from "~/assets/icons/universe.svg?inline"; const appsPerNetwork = { mainnet: [ @@ -98,7 +99,14 @@ const appsPerNetwork = { name: "Yearn", url: "/mainnet/yearn-v2", description: "Automated Yield Strategies" - } + }, + { + id: "universe", + icon: UniverseIcon, + name: "Universe", + url: "/mainnet/universe", + description: "Maximizing your Uniswap V3 Return" + }, ], polygon: [ { diff --git a/pages/mainnet/universe.vue b/pages/mainnet/universe.vue new file mode 100644 index 0000000..7ea9d47 --- /dev/null +++ b/pages/mainnet/universe.vue @@ -0,0 +1,167 @@ + + + From 4c1efc2e77659867572c559c9a402f39bc74fdd0 Mon Sep 17 00:00:00 2001 From: UniverseFinance Date: Fri, 3 Dec 2021 17:13:18 +0800 Subject: [PATCH 2/4] switch to prod api --- composables/protocols/useUniverseOverview.ts | 2 +- composables/protocols/useUniversePosition.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composables/protocols/useUniverseOverview.ts b/composables/protocols/useUniverseOverview.ts index 26bab20..9cf9b80 100644 --- a/composables/protocols/useUniverseOverview.ts +++ b/composables/protocols/useUniverseOverview.ts @@ -15,7 +15,7 @@ export function useUniverseOverview() { const fetchOverview = async () => { const { data } = await $axios - .$get<{ data: OverviewModel }>("https://defi-test-api.webwxk.com/singleVault/universe/instadapp/overview") + .$get<{ data: OverviewModel }>("https://api.webwxk.com/singleVault/universe/instadapp/overview") overview.value = data; diff --git a/composables/protocols/useUniversePosition.ts b/composables/protocols/useUniversePosition.ts index 00a0beb..bc123e7 100644 --- a/composables/protocols/useUniversePosition.ts +++ b/composables/protocols/useUniversePosition.ts @@ -49,7 +49,7 @@ export function useUniversePosition() { const fetchPosition = async () => { const { data: availableVaults } = await $axios - .$get<{ data: VaultModel[] }>("https://defi-test-api.webwxk.com/singleVault/universe/instadapp/vaultList") + .$get<{ data: VaultModel[] }>("https://api.webwxk.com/singleVault/universe/instadapp/vaultList") if (!library.value) { return; From 1168467ee2873f9f4c92968937a07108f842d432 Mon Sep 17 00:00:00 2001 From: zuber Date: Tue, 14 Dec 2021 16:39:46 +0800 Subject: [PATCH 3/4] ux enhancements: change position vaule & position amount --- components/protocols/CardUniverse.vue | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/components/protocols/CardUniverse.vue b/components/protocols/CardUniverse.vue index 7f2c9d8..ecf4f55 100644 --- a/components/protocols/CardUniverse.vue +++ b/components/protocols/CardUniverse.vue @@ -19,10 +19,11 @@
- {{ formatDecimal(supply) }} {{ symbol }} + {{ formatUsd(supplyUsd) }}
- {{ formatUsd(supplyUsd) }} + {{ formatDecimal(supply) }} {{ symbol }} +
-

{{ formatPercent(feeAprLifetime) }}

+

{{ formatPercent(feeAprLifetime) }}

Fee APR

From ddeedcf8e609d7f53eae6ad0750cb810aa33ad52 Mon Sep 17 00:00:00 2001 From: UniverseFinance Date: Fri, 24 Dec 2021 10:32:46 +0800 Subject: [PATCH 4/4] fix: percent can be gt 100 --- components/protocols/CardUniverse.vue | 2 +- composables/useFormatting.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/protocols/CardUniverse.vue b/components/protocols/CardUniverse.vue index ecf4f55..d28125c 100644 --- a/components/protocols/CardUniverse.vue +++ b/components/protocols/CardUniverse.vue @@ -42,7 +42,7 @@
-

{{ formatPercent(feeAprLifetime) }}

+

{{ formatPercent(feeAprLifetime, 2, true) }}

Fee APR

diff --git a/composables/useFormatting.ts b/composables/useFormatting.ts index 4bbf8d2..519faee 100644 --- a/composables/useFormatting.ts +++ b/composables/useFormatting.ts @@ -32,9 +32,9 @@ export function useFormatting() { } } - function formatPercent(value: any, fractionDigits = 2) { + function formatPercent(value: any, fractionDigits = 2, noLimit = false) { if (isZero(value)) return "0%"; - if (gt(value, 1)) return ">100%"; + if (!noLimit && gt(value, 1)) return ">100%"; const formatter = new Intl.NumberFormat(locale, { style: "percent",