import { defineComponent, computed } from '@nuxtjs/composition-api'
-import SVGSearch from '~/assets/icons/search.svg?inline'
+import SVGSearch from '@/assets/icons/search.svg?inline'
export default defineComponent({
props: {
@@ -37,3 +37,37 @@ export default defineComponent({
},
})
+
diff --git a/components/sidebar/context/SidebarContext.vue b/components/sidebar/context/SidebarContext.vue
new file mode 100644
index 0000000..ec7a044
--- /dev/null
+++ b/components/sidebar/context/SidebarContext.vue
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/SidebarContextContainer.vue b/components/sidebar/context/SidebarContextContainer.vue
new file mode 100644
index 0000000..c852ea4
--- /dev/null
+++ b/components/sidebar/context/SidebarContextContainer.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/components/sidebar/context/SidebarContextHeader.vue b/components/sidebar/context/SidebarContextHeader.vue
new file mode 100644
index 0000000..a9902d9
--- /dev/null
+++ b/components/sidebar/context/SidebarContextHeader.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
diff --git a/components/sidebar/context/SidebarContextRootContainer.vue b/components/sidebar/context/SidebarContextRootContainer.vue
new file mode 100644
index 0000000..bba6d0e
--- /dev/null
+++ b/components/sidebar/context/SidebarContextRootContainer.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/aaveV2/SidebarAaveV2Supply.vue b/components/sidebar/context/aaveV2/SidebarAaveV2Supply.vue
new file mode 100644
index 0000000..0fa0cd8
--- /dev/null
+++ b/components/sidebar/context/aaveV2/SidebarAaveV2Supply.vue
@@ -0,0 +1,84 @@
+
+
+ Supply SYMBOL
+
+
+
+ BALANCE
+
+
+
+
+
+
+
+
+
+
+ Projected Debt Position
+
+
+
+
+
+
+
+
+
+
+ {{ formatUsdMax(liquidationPrice, liquidationMaxPrice) }} / {{ formatUsd(liquidationMaxPrice) }}
+
+
+
+
+
+ Supply
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/CurrencyList.vue b/components/sidebar/context/components/CurrencyList.vue
new file mode 100644
index 0000000..2c9317c
--- /dev/null
+++ b/components/sidebar/context/components/CurrencyList.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/CustomBlockCard.vue b/components/sidebar/context/components/CustomBlockCard.vue
new file mode 100644
index 0000000..603149e
--- /dev/null
+++ b/components/sidebar/context/components/CustomBlockCard.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
Input
+
{{ inputAmount }} {{ inputSymbol }}
+
+
+
Output
+
{{ outputAmount }} {{ outputSymbol }}
+
+
+
+
+
+
+
+ Price Impact {{ slippageAmount }}%
+
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/CustomBlocksList.vue b/components/sidebar/context/components/CustomBlocksList.vue
new file mode 100644
index 0000000..2143dab
--- /dev/null
+++ b/components/sidebar/context/components/CustomBlocksList.vue
@@ -0,0 +1,41 @@
+
+
+
+
{{ protocol.name }}
+
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/CustomTempBlockCard.vue b/components/sidebar/context/components/CustomTempBlockCard.vue
new file mode 100644
index 0000000..5d2ccd6
--- /dev/null
+++ b/components/sidebar/context/components/CustomTempBlockCard.vue
@@ -0,0 +1,7 @@
+
+
+
New block will be added here
+
+
diff --git a/components/sidebar/context/components/SidebarRateTypeSelect.vue b/components/sidebar/context/components/SidebarRateTypeSelect.vue
new file mode 100644
index 0000000..7f48c61
--- /dev/null
+++ b/components/sidebar/context/components/SidebarRateTypeSelect.vue
@@ -0,0 +1,71 @@
+
+
+
+ APR type
+
+
+
+
+
+
Variable rate only support
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/SidebarSectionStatus.vue b/components/sidebar/context/components/SidebarSectionStatus.vue
new file mode 100644
index 0000000..d1f3db8
--- /dev/null
+++ b/components/sidebar/context/components/SidebarSectionStatus.vue
@@ -0,0 +1,38 @@
+
+
+
+
Status (max. {{ formatPercent(liquidation) }})
+
+
{{ text }}
+
+
+
+
{{ formatPercent(status) }}
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/SidebarSectionValueWithIcon.vue b/components/sidebar/context/components/SidebarSectionValueWithIcon.vue
new file mode 100644
index 0000000..dbdcbf1
--- /dev/null
+++ b/components/sidebar/context/components/SidebarSectionValueWithIcon.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/SidebarTokensList.vue b/components/sidebar/context/components/SidebarTokensList.vue
new file mode 100644
index 0000000..0391245
--- /dev/null
+++ b/components/sidebar/context/components/SidebarTokensList.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+ {{ token.name }}
+
+
{{ token.symbol }}
+
+
+
+
+ {{ formatDecimal(token.balance) }}
+
+
{{ formatUsd(token.netWorth) }}
+
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/StrategyCheckoutCard.vue b/components/sidebar/context/components/StrategyCheckoutCard.vue
new file mode 100644
index 0000000..914d397
--- /dev/null
+++ b/components/sidebar/context/components/StrategyCheckoutCard.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
{{ totalPrice | formatUsd }}
+
{{ tokenAmount }} {{ symbol }}
+
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/ValueDisplay.vue b/components/sidebar/context/components/ValueDisplay.vue
new file mode 100644
index 0000000..dfedb1a
--- /dev/null
+++ b/components/sidebar/context/components/ValueDisplay.vue
@@ -0,0 +1,38 @@
+
+
+
{{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/sidebar/context/components/ValueDisplayLabel.vue b/components/sidebar/context/components/ValueDisplayLabel.vue
new file mode 100644
index 0000000..08f04f3
--- /dev/null
+++ b/components/sidebar/context/components/ValueDisplayLabel.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/composables/useCustomBlocks.ts b/composables/useCustomBlocks.ts
new file mode 100644
index 0000000..ed15c2d
--- /dev/null
+++ b/composables/useCustomBlocks.ts
@@ -0,0 +1,107 @@
+import { ref, computed, useContext } from "@nuxtjs/composition-api";
+import { useSidebarBlockData } from "~/composables/useSidebarBlockData";
+
+export function useCustomBlocks() {
+ const { app, store } = useContext();
+ const { getBlockData } = useSidebarBlockData();
+ const search = ref("");
+
+ const filteredBlocks = computed(() => {
+ if (!search.value) return protocolsList.value;
+
+ const arrCopy = JSON.parse(JSON.stringify(protocolsList.value));
+ const result = [];
+ const term = search.value.toLowerCase();
+ const re = new RegExp(term, "i");
+
+ arrCopy.forEach(protocol => {
+ const filteredBlocks = protocol.blocks.filter(block =>
+ re.test(block.name)
+ );
+
+ if (filteredBlocks.length) {
+ protocol.blocks = filteredBlocks;
+ result.push(protocol);
+ }
+ });
+
+ return result;
+ });
+
+ function startNewBlockSetup(protocol, block) {
+ createNewBlock(protocol, block);
+ openBlockSetupSidebar(block.type);
+ }
+
+ function startBlockEdit(block) {
+ store.dispatch("custom-strategy/setSelectedBlock", {
+ type: block.type,
+ blockData: block
+ });
+ openBlockSetupSidebar(block.type);
+ }
+
+ function createNewBlock(protocol, block) {
+ const blockData = { protocol, id: -1 };
+ store.dispatch("custom-strategy/setSelectedBlock", {
+ type: block.type,
+ blockData
+ });
+ }
+
+ function openBlockSetupSidebar(type) {
+ const blockData = getBlockData(type);
+ if (blockData && blockData.hash) {
+ app.router.push({
+ hash: blockData.hash
+ });
+ }
+ }
+
+ const protocolsList = ref([
+ {
+ name: "Uniswap",
+ tokenKey: "uni",
+ blocks: [
+ { name: "Swap Token", type: "swap-token" },
+ { name: "Add Liqudity" },
+ { name: "Remove Liquidity" }
+ ]
+ },
+ {
+ name: "Aave",
+ tokenKey: "aave",
+ blocks: [
+ { name: "Flash Loan", type: "flashloan" },
+ { name: "Deposit" },
+ { name: "Withdraw" }
+ ]
+ },
+ {
+ name: "Makerdao",
+ tokenKey: "mkr",
+ blocks: [
+ { name: "Swap Token", type: "swap-token" },
+ { name: "Add Liqudity" },
+ { name: "Remove Liquidity" }
+ ]
+ },
+ {
+ name: "Compound",
+ tokenKey: "comp",
+ blocks: [
+ { name: "Flash Loan", type: "flashloan" },
+ { name: "Deposit" },
+ { name: "Withdraw" }
+ ]
+ }
+ ]);
+
+ return {
+ search,
+ filteredBlocks,
+ startNewBlockSetup,
+ openBlockSetupSidebar,
+ startBlockEdit
+ };
+}
diff --git a/composables/useLink.ts b/composables/useLink.ts
new file mode 100644
index 0000000..f98a69f
--- /dev/null
+++ b/composables/useLink.ts
@@ -0,0 +1,21 @@
+import { computed } from '@nuxtjs/composition-api'
+import { useWeb3 } from './useWeb3'
+
+export function useLink() {
+ const { networkName } = useWeb3()
+
+ const addressDetailsLink = computed(() => {
+ if (networkName.value === 'polygon') {
+ return 'https://polygonscan.com/address'
+ }
+
+ return 'https://etherscan.io/address'
+ })
+
+ return { addressDetailsLink }
+}
+
+export const getEtherscanLink = (transactionHash) => `https://etherscan.io/tx/${transactionHash}`
+export const getMaticLink = (transactionHash) => `https://polygonscan.com/tx/${transactionHash}`
+export const getTenderlyLink = (simulationId) =>
+ `https://dashboard.tenderly.co/public/InstaDApp/dsa-simulations/fork-simulation/${simulationId}?hideSidebar=true`
diff --git a/composables/useNotification.ts b/composables/useNotification.ts
new file mode 100644
index 0000000..0b1a6f4
--- /dev/null
+++ b/composables/useNotification.ts
@@ -0,0 +1,170 @@
+import { ref } from "@nuxtjs/composition-api";
+import { useFormatting } from "@/composables/useFormatting";
+import { getEtherscanLink, getMaticLink, getTenderlyLink } from "./useLink";
+import { useRandom } from "./useRandom";
+const { makeid } = useRandom();
+
+const queue = ref([]);
+
+export function useNotification() {
+ const { shortenHash } = useFormatting();
+
+ function close(key) {
+ queue.value = queue.value.filter(item => item.key !== key);
+ }
+
+ function closeAll() {
+ queue.value.forEach((item, index) => {
+ setTimeout(() => {
+ queue.value.shift();
+ }, index * 150);
+ });
+ }
+
+ function closeAwaiting(title, success = true, key) {
+ const found = queue.value.find(item => item.key === key);
+ if (!found) return;
+
+ if (success) {
+ found.icon = "success";
+ found.title = title;
+ } else {
+ found.icon = "error";
+ found.title = title;
+ }
+
+ setTimeout(() => {
+ close(key);
+ }, 2000);
+ }
+
+ /**
+ * Ques a notification to show to the user.
+ *
+ * @param {string} params.icon Icon of notification
+ * @param {string} params.title Title of notification
+ * @param {string} params.body Body text of notification
+ * @param {string} params.href Link of notification body
+ * @param {number} params.duration Duration in ms. 0 for no timeout
+ * @param {string} params.key key for notification identification
+ */
+ function show(params) {
+ if (params) {
+ if (!params.key) params.key = makeid(10);
+ queue.value.push(params);
+ }
+ }
+
+ function showError(title, body, href) {
+ show({ icon: "error", title, body, href, duration: 0 });
+ }
+
+ function showWarning(title, body) {
+ show({ icon: "warning", title, body, duration: 0 });
+ }
+
+ function showNotImplemented() {
+ show({
+ icon: "warning",
+ title: "Not implemented",
+ body: "This feature is not yet implemented."
+ });
+ }
+
+ function showSuccess(title, body) {
+ show({ icon: "success", title, body, duration: 5000 });
+ }
+
+ function showInfo(title, body) {
+ show({ icon: "info", title, body, duration: 7000 });
+ }
+
+ function showAwaiting(title, body) {
+ const key = makeid(10);
+ show({ icon: "spinner", title, body, duration: 0, key });
+ return key;
+ }
+
+ function showPendingTransaction(transactionHash, network) {
+ let href;
+ if (network === "matic") {
+ href = getMaticLink(transactionHash);
+ } else {
+ href = getEtherscanLink(transactionHash);
+ }
+ const body = shortenHash(transactionHash);
+
+ show({
+ icon: "pending-transaction",
+ title: "Pending transaction",
+ href,
+ body,
+ duration: 0,
+ key: transactionHash
+ });
+ }
+
+ function showConfirmedTransaction(transactionHash, network) {
+ let href;
+ if (network === "matic") {
+ href = getMaticLink(transactionHash);
+ } else {
+ href = getEtherscanLink(transactionHash);
+ }
+ const body = shortenHash(transactionHash);
+
+ const found = queue.value.find(item => item.key === transactionHash);
+ if (found) {
+ found.icon = "success";
+ found.title = "Transaction Confirmed";
+ found.href = href;
+ found.body = body;
+ } else {
+ show({
+ icon: "success",
+ title: "Transaction Confirmed",
+ href,
+ body,
+ duration: 0
+ });
+ }
+ }
+
+ function showConfirmedSimulation(transactionId) {
+ const href = getTenderlyLink(transactionId);
+ const body = shortenHash(transactionId);
+
+ show({
+ icon: "success",
+ title: "Successfully Simulated",
+ href,
+ body,
+ duration: 5000
+ });
+ }
+
+ function showLoggedIn(title, body) {
+ show({ icon: "logged-in", title, body, duration: 2000 });
+ }
+ function showLoggedOut(title, body) {
+ show({ icon: "logged-out", title, body, duration: 2000 });
+ }
+
+ return {
+ queue,
+ close,
+ closeAwaiting,
+ showError,
+ showWarning,
+ showNotImplemented,
+ showSuccess,
+ showInfo,
+ showPendingTransaction,
+ showConfirmedTransaction,
+ showConfirmedSimulation,
+ showLoggedIn,
+ showLoggedOut,
+ showAwaiting,
+ closeAll
+ };
+}
diff --git a/composables/useProtocolData.ts b/composables/useProtocolData.ts
new file mode 100644
index 0000000..0a9d32b
--- /dev/null
+++ b/composables/useProtocolData.ts
@@ -0,0 +1,83 @@
+// @ts-nocheck
+import SVGCompound from "@/assets/logo/compound.svg?inline";
+import SVGMakerDAO from "@/assets/logo/makerdao.svg?inline";
+import SVGAave from "@/assets/logo/aave.svg?inline";
+import SVGAaveV2 from "@/assets/logo/aave-v2.svg?inline";
+import SVGUniswap from "@/assets/logo/uniswap.svg?inline";
+import SVGInstadapp from "@/assets/logo/instadapp-logo-icon.svg?inline";
+import SVGDefault from "@/assets/logo/default.svg?inline";
+import { computed } from "@nuxtjs/composition-api";
+
+const protocols = {
+ makerdao: {
+ title: "MakerDAO",
+ logo: SVGMakerDAO,
+ protocol: "makerdao",
+ supportedNetworks: ["mainnet"]
+ },
+ compound: {
+ title: "Compound",
+ logo: SVGCompound,
+ protocol: "compound",
+ supportedNetworks: ["mainnet"]
+ },
+ aave: {
+ title: "Aave",
+ logo: SVGAave,
+ protocol: "aave",
+ supportedNetworks: ["mainnet"]
+ },
+ "aave-v2": {
+ title: "Aave V2",
+ logo: SVGAaveV2,
+ protocol: "aave-v2",
+ supportedNetworks: ["mainnet", "matic"]
+ },
+ uniswap: {
+ title: "Uniswap",
+ logo: SVGUniswap,
+ protocol: "uniswap",
+ supportedNetworks: ["mainnet"]
+ },
+ instadapp: {
+ title: "Instadapp",
+ logo: SVGInstadapp,
+ protocol: "guniswap",
+ supportedNetworks: ["mainnet"]
+ },
+ default: {
+ title: "Balance",
+ logo: SVGDefault,
+ protocol: "default",
+ supportedNetworks: ["mainnet"]
+ }
+};
+
+export function useProtocolData(protocol) {
+ protocol = String(protocol).toLowerCase();
+
+ const data = computed(() => protocols[protocol]);
+ const title = computed(() => (data.value ? data.value.title : null));
+ const logo = computed(() => (data.value ? data.value.logo : null));
+
+ const protocolName = computed(() =>
+ data.value ? data.value.protocol : null
+ );
+
+ const leverageableProtocols = computed(() =>
+ //@ts-ignore
+ Object.values(protocols).filter(protocol => protocol.supportLeverageEth)
+ );
+
+ function networkIsSupported(network) {
+ return data.value.supportedNetworks.includes(network);
+ }
+
+ return {
+ title,
+ logo,
+ protocolName,
+ leverageableProtocols,
+ networkIsSupported
+ };
+}
diff --git a/composables/useRandom.ts b/composables/useRandom.ts
new file mode 100644
index 0000000..5c1bfd9
--- /dev/null
+++ b/composables/useRandom.ts
@@ -0,0 +1,20 @@
+export function useRandom() {
+ function makeid(length) {
+ let result = "";
+ const characters =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ const charactersLength = characters.length;
+ for (let i = 0; i < length; i++) {
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
+ }
+ return result;
+ }
+
+ function getRandomInt(min, max) {
+ min = Math.ceil(min);
+ max = Math.floor(max);
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+ }
+
+ return { makeid, getRandomInt };
+}
diff --git a/composables/useSidebar.ts b/composables/useSidebar.ts
new file mode 100644
index 0000000..be68f5d
--- /dev/null
+++ b/composables/useSidebar.ts
@@ -0,0 +1,106 @@
+import {
+ computed,
+ nextTick,
+ ref,
+ useContext,
+ useRouter,
+ watch
+} from "@nuxtjs/composition-api";
+
+import SidebarAaveV2Supply from "~/components/sidebar/context/aaveV2/SidebarAaveV2Supply.vue";
+import { useDSA } from "./useDSA";
+import { useWeb3 } from "./useWeb3";
+// import SidebarAaveV2Borrow from '~/components/sidebar/context/aaveV2/SidebarAaveV2Borrow.vue'
+// import SidebarAaveV2Payback from '~/components/sidebar/context/aaveV2/SidebarAaveV2Payback.vue'
+// import SidebarAaveV2Withdraw from '~/components/sidebar/context/aaveV2/SidebarAaveV2Withdraw.vue'
+
+const sidebars = {
+ "/polygon/aave-v2#overview": { component: null },
+ "/polygon/aave-v2#supply": { component: SidebarAaveV2Supply },
+ "/polygon/aave-v2#borrow": { component: null },
+ "/polygon/aave-v2#payback": { component: null },
+ "/polygon/aave-v2#withdraw": { component: null },
+ "/polygon/aave-v2#withdraw-token": {
+ component: null,
+ back: { hash: "withdraw-overview" }
+ }
+};
+
+const sidebar = ref(null);
+const props = ref(null);
+
+export function init() {
+ const { route } = useContext();
+ const router = useRouter()
+ const { active } = useWeb3();
+ const { dsa } = useDSA();
+
+ watch(
+ [route, active, dsa],
+ async ([route, active, dsa], [oldRoute, oldActive, oldDsa]) => {
+ await nextTick();
+
+ const hasPathChanged = !oldRoute || route.path !== oldRoute.path;
+ const hasIsLoggedInChanged = active !== oldActive;
+ const hasDsaChanged = dsa !== oldDsa;
+
+ const [hash, params] = route.hash.split("?");
+
+ if (hasPathChanged){
+ router.push({ hash: null })
+ return
+ }
+
+ sidebar.value = sidebars[route.path + hash] || sidebars[hash];
+
+ if (!sidebar.value) {
+ props.value = {};
+ return;
+ }
+
+ if (!params) {
+ props.value = {};
+ return;
+ }
+
+ // parse url params (example: `prop1=value1&prop2=value2`)
+ props.value = params.split("&").reduce((props, entry) => {
+ const [key, value] = entry.split("=");
+ props[key] = value;
+ return props;
+ }, {});
+ },
+ { immediate: true }
+ );
+}
+
+export function useSidebar() {
+ const { route } = useContext();
+ const router = useRouter();
+
+ function close() {
+ router.push({ hash: null });
+ }
+
+ function back() {
+ const location = sidebar.value?.back || { hash: null };
+
+ router.push(location);
+ }
+
+ const component = computed(() => sidebar.value?.component);
+
+ const isOpen = computed(() => {
+ if (!route.value.hash) return false;
+
+ return !!component.value;
+ });
+
+ return {
+ close,
+ back,
+ component,
+ props,
+ isOpen
+ };
+}
diff --git a/composables/useSidebarBlockData.ts b/composables/useSidebarBlockData.ts
new file mode 100644
index 0000000..a68c913
--- /dev/null
+++ b/composables/useSidebarBlockData.ts
@@ -0,0 +1,33 @@
+import { useNotification } from "@/composables/useNotification";
+import SwapTokenBlock from "@/core/entity/swap-token-block";
+
+const { showNotImplemented } = useNotification();
+
+const blocks = {
+ "swap-token": {
+ hash: "setup-swap-token",
+ entity: SwapTokenBlock
+ }
+};
+
+export function useSidebarBlockData() {
+ function getBlockData(type) {
+ const block = blocks[type];
+ const hash = block ? block.hash : null;
+
+ return { hash };
+ }
+
+ function createBlockFactory({ type, blockData }) {
+ const block = blocks[type];
+ const BlockEntity = block ? block.entity : null;
+
+ if (BlockEntity) {
+ return new BlockEntity(blockData);
+ } else {
+ showNotImplemented();
+ }
+ }
+
+ return { getBlockData, createBlockFactory };
+}
diff --git a/composables/useStatus.ts b/composables/useStatus.ts
new file mode 100644
index 0000000..2b145a9
--- /dev/null
+++ b/composables/useStatus.ts
@@ -0,0 +1,52 @@
+import { computed } from '@nuxtjs/composition-api'
+import { useBigNumber } from './useBigNumber'
+const { lt, gte, lte, isZero } = useBigNumber()
+
+const colorValues = [
+ { minValue: 1.0, color: 'red-dark' },
+ { minValue: 0.9, color: 'red' },
+ { minValue: 0.85, color: 'passion-orange' },
+ { minValue: 0.8, color: 'orange' },
+ { minValue: 0.75, color: 'yellow' },
+ { minValue: 0.0, color: 'green-pure' },
+]
+
+const textValues = [
+ { minValue: 1, text: 'Liquidate' },
+ { minValue: 0.9, text: 'Very Risky' },
+ { minValue: 0.75, text: 'Risky' },
+ { minValue: 0, text: 'Safe' },
+]
+
+export function useStatus(statusLiquidationRatioRef, statusRef, liquidationRef) {
+ const color = computed(() => {
+ if (!isZero(statusRef) && !isZero(liquidationRef)) {
+ if (gte(statusRef.value, '1')) return 'red-dark'
+ }
+
+ if (lte(statusLiquidationRatioRef.value, '0')) return 'grey'
+ const colorValue = colorValues.find((colorValue) => gte(statusLiquidationRatioRef.value, colorValue.minValue))
+
+ return colorValue.color
+ })
+
+ const text = computed(() => {
+ if (!isZero(statusRef) && !isZero(liquidationRef)) {
+ if (gte(statusRef.value, '1')) return 'Liquidate'
+ }
+
+ if (lte(statusLiquidationRatioRef.value, '0')) return 'No position'
+
+ const textValue = textValues.find((textValue) => gte(statusLiquidationRatioRef.value, textValue.minValue))
+
+ if (!textValue) return 'No position'
+
+ return textValue.text
+ })
+
+ return { color, text }
+}
+
+export function getSlippageBadgeColor(value) {
+ return lt(value, '0.02') ? 'green' : 'red'
+}
diff --git a/core/entity/protocol.ts b/core/entity/protocol.ts
new file mode 100644
index 0000000..2a45422
--- /dev/null
+++ b/core/entity/protocol.ts
@@ -0,0 +1,12 @@
+//@ts-nocheck
+export default class Protocol {
+ constructor(data) {
+ if (data) {
+ this.name = data.name;
+ this.tokenKey = data.tokenKey;
+ } else {
+ this.name = "";
+ this.tokenKey = "";
+ }
+ }
+}
diff --git a/core/entity/swap-token-block.ts b/core/entity/swap-token-block.ts
new file mode 100644
index 0000000..e4811b6
--- /dev/null
+++ b/core/entity/swap-token-block.ts
@@ -0,0 +1,25 @@
+//@ts-nocheck
+import Protocol from '@/core/entity/protocol'
+
+export default class SwapTokenBlock {
+ constructor(data) {
+ if (data) {
+ this.name = 'Swap Token'
+ this.type = 'swap-token'
+ this.id = data.id
+ this.protocol = new Protocol(data.protocol)
+ this.inputAmount = Number(data.inputAmount) || 0
+ this.inputTokenKey = this._setInputTokenKey(data.inputTokenKey)
+ this.outputAmount = Number(data.outputAmount) || 0
+ this.outputTokenKey = data.outputTokenKey
+ this.slippage = Number(data.slippage) || 0
+ }
+ }
+
+ _setInputTokenKey(tokenKey) {
+ if (!tokenKey) {
+ return this.protocol.tokenKey
+ }
+ return tokenKey
+ }
+}
diff --git a/layouts/default.vue b/layouts/default.vue
index 497e0d5..3c91b9e 100644
--- a/layouts/default.vue
+++ b/layouts/default.vue
@@ -1,11 +1,14 @@
-
+
-
@@ -15,7 +18,7 @@ import MakerDAOIcon from '~/assets/icons/makerdao.svg?inline'
import CompoundIcon from '~/assets/icons/compound.svg?inline'
import AaveIcon from '~/assets/icons/aave.svg?inline'
import { useWeb3 } from '~/composables/useWeb3'
-
+import { init as initSidebars } from '~/composables/useSidebar'
export default defineComponent({
components: {
MakerDAOIcon,
@@ -25,6 +28,8 @@ export default defineComponent({
setup() {
const { active, activate, deactivate } = useWeb3();
+ initSidebars();
+
return {
active,
activate,
@@ -33,4 +38,27 @@ export default defineComponent({
}
})
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/nuxt.config.js b/nuxt.config.js
index faa89e8..916eb5d 100644
--- a/nuxt.config.js
+++ b/nuxt.config.js
@@ -32,6 +32,7 @@ export default {
plugins: [
"~/plugins/v-click-outside.js",
"~/plugins/web3modal.js",
+ { src: '~/plugins/v-tooltip', mode: 'client' },
],
// Auto import components: https://go.nuxtjs.dev/config-components
diff --git a/package.json b/package.json
index 9d3f2e8..c39bd17 100644
--- a/package.json
+++ b/package.json
@@ -16,9 +16,11 @@
"@walletconnect/web3-provider": "^1.4.1",
"bignumber.js": "^9.0.1",
"core-js": "^3.15.1",
+ "css-color-function": "^1.3.3",
"dsa-connect": "^0.4.2",
"nuxt": "^2.15.7",
"v-click-outside": "^3.1.2",
+ "v-tooltip": "^2.1.3",
"walletlink": "^2.1.6",
"web3": "^1.4.0",
"web3modal": "^1.9.3"
diff --git a/plugins/v-tooltip/index.js b/plugins/v-tooltip/index.js
new file mode 100644
index 0000000..18cdeed
--- /dev/null
+++ b/plugins/v-tooltip/index.js
@@ -0,0 +1,12 @@
+import Vue from 'vue'
+import { VTooltip, VPopover, VClosePopover } from 'v-tooltip'
+import './v-tooltip.css'
+
+VTooltip.options.defaultOffset = 4
+VTooltip.options.defaultDelay = 150
+VTooltip.options.autoHide = false
+VTooltip.options.defaultTrigger = 'hover focus click'
+
+Vue.directive('tooltip', VTooltip)
+Vue.directive('close-popover', VClosePopover)
+Vue.component('VPopover', VPopover)
diff --git a/plugins/v-tooltip/v-tooltip.css b/plugins/v-tooltip/v-tooltip.css
new file mode 100644
index 0000000..514fbc2
--- /dev/null
+++ b/plugins/v-tooltip/v-tooltip.css
@@ -0,0 +1,73 @@
+.tooltip {
+ @apply z-50 block;
+ @apply max-w-xs;
+}
+.tooltip .tooltip-inner {
+ @apply px-4 py-2 font-medium rounded bg-navi-pure text-light text-14;
+}
+.tooltip .tooltip-arrow {
+ z-index: 1;
+ @apply absolute w-0 h-0 m-1 border border-navi-pure;
+}
+.dark .tooltip .tooltip-inner {
+ @apply bg-dark-300 text-light;
+}
+.dark .tooltip .tooltip-arrow {
+ @apply border-dark-300;
+}
+.tooltip[x-placement^='top'] {
+ @apply mb-1;
+}
+.tooltip[x-placement^='top'] .tooltip-arrow {
+ border-width: 4px 4px 0 4px;
+ border-left-color: transparent !important;
+ border-right-color: transparent !important;
+ border-bottom-color: transparent !important;
+ bottom: -4px;
+ left: calc(50% - 4px);
+ @apply my-0;
+}
+.tooltip[x-placement^='bottom'] {
+ @apply mt-1;
+}
+.tooltip[x-placement^='bottom'] .tooltip-arrow {
+ border-width: 0 4px 4px 4px;
+ border-left-color: transparent !important;
+ border-right-color: transparent !important;
+ border-top-color: transparent !important;
+ top: -4px;
+ left: calc(50% - 4px);
+ @apply my-0;
+}
+.tooltip[x-placement^='right'] {
+ @apply ml-1;
+}
+.tooltip[x-placement^='right'] .tooltip-arrow {
+ border-width: 4px 4px 4px 0;
+ border-left-color: transparent !important;
+ border-top-color: transparent !important;
+ border-bottom-color: transparent !important;
+ left: -4px;
+ top: calc(50% - 4px);
+ @apply mx-0;
+}
+.tooltip[x-placement^='left'] {
+ @apply mr-1;
+}
+.tooltip[x-placement^='left'] .tooltip-arrow {
+ border-width: 4px 0 4px 4px;
+ border-top-color: transparent !important;
+ border-right-color: transparent !important;
+ border-bottom-color: transparent !important;
+ right: -4px;
+ top: calc(50% - 4px);
+ @apply mx-0;
+}
+.tooltip[aria-hidden='true'] {
+ transition: opacity 0.15s, visibility 0.15s;
+ @apply invisible opacity-0;
+}
+.tooltip[aria-hidden='false'] {
+ @apply visible opacity-100;
+ transition: opacity 0.15s;
+}
diff --git a/tailwind.config.js b/tailwind.config.js
index 63eeb44..25cec87 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,12 +1,23 @@
const defaultTheme = require("tailwindcss/defaultTheme");
-
module.exports = {
mode: 'jit',
purge: [],
- darkMode: false, // or 'media' or 'class'
+ dark: 'class',
theme: {
extend: {
+ fontSize: {
+ 9: ['9px', '10px'],
+ 11: ['11px', '14px'],
+ 12: ['12px', '14px'],
+ 14: ['14px', '17px'],
+ 16: ['16px', '19.5px'],
+ 18: ['18px', '21px'],
+ 19: ['19px', '23px'],
+ 21: ['21px', '25.6px'],
+ 24: ['24px', '28.8px'],
+ 32: ['32px', '39.01px'],
+ },
colors: {
primary: {
black: "#161E2E",
@@ -15,7 +26,86 @@ module.exports = {
dark: "#3F75FF",
hover: "#5E8BFF",
}
- }
+ },
+ lightest: '#FEFEFF',
+ light: '#FCFCFC',
+ background: 'rgb(245, 246, 250)',
+ 'background-light': '#FBFCFD',
+ 'background-dark': '#2C3D53',
+ selection: '#F8F9FE',
+ brand: '#11263F', // dark.600
+
+ dark: {
+ 300: '#2c50a0',
+ 400: '#244166',
+ 500: '#152e4d',
+ 600: '#12263f',
+ },
+
+ purple: {
+ pure: '#5242A2',
+ light: '#EEECF6',
+ },
+ turquese: {
+ pure: '#2EBDC2',
+ light: '#EAF8F9',
+ },
+ green: {
+ pure: '#32C34A',
+ light: '#EBF9ED',
+ },
+ red: {
+ pure: '#E63959',
+ ...defaultTheme.colors.red,
+ },
+ blue: {
+ pure: '#3997C5',
+ light: '#E9F3F8',
+ },
+ 'ocean-blue': {
+ pure: '#3F75FF',
+ light: '#ECF1FF',
+ },
+ yellow: {
+ pure: '#F3C024',
+ light: '#FEF8E7',
+ },
+ orange: {
+ pure: '#F08642',
+ light: '#FDF3EC',
+ },
+ 'passion-orange': {
+ pure: '#FF6600',
+ light: '#FFF0E5',
+ },
+ 'light-brown': {
+ pure: '#FFC888',
+ light: '#FFF9F3',
+ },
+ brown: {
+ pure: '#CA8700',
+ light: '#FAF3E5',
+ },
+ navi: {
+ pure: '#131E40',
+ 'pure-light': '#2D3755',
+ lighter: '#12263F',
+ light: 'rgba(19, 30, 64, 0.1)',
+ },
+ grey: {
+ pure: '#A5ADC6',
+ light: '#e9ecf2',
+ dark: '#556D9C',
+ },
+ },
+ opacity: {
+ 10: '0.10',
+ 17: '0.17',
+ 20: '0.20',
+ 38: '0.38',
+ 70: '0.7',
+ 84: '0.84',
+ 90: '0.90',
},
fontFamily: {
sans: ["Montserrat", ...defaultTheme.fontFamily.sans]
diff --git a/yarn.lock b/yarn.lock
index 77a29cc..c090ed7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -864,6 +864,13 @@
"@babel/types" "^7.4.4"
esutils "^2.0.2"
+"@babel/runtime@^7.13.10":
+ version "7.14.8"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446"
+ integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/runtime@^7.14.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4":
version "7.14.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
@@ -2859,6 +2866,11 @@ backoff@^2.5.0:
dependencies:
precond "0.2"
+balanced-match@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
+ integrity sha1-tQS9BYabOSWd0MXvw12EMXbczEo=
+
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -3594,6 +3606,11 @@ clone-response@^1.0.2:
dependencies:
mimic-response "^1.0.0"
+clone@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+ integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+
clone@^2.0.0, clone@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
@@ -3626,7 +3643,7 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
-color-convert@^1.9.0:
+color-convert@^1.3.0, color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@@ -3650,6 +3667,13 @@ color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+color-string@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
+ integrity sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=
+ dependencies:
+ color-name "^1.0.0"
+
color-string@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312"
@@ -3658,6 +3682,15 @@ color-string@^1.6.0:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
+color@^0.11.0:
+ version "0.11.4"
+ resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
+ integrity sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=
+ dependencies:
+ clone "^1.0.2"
+ color-convert "^1.3.0"
+ color-string "^0.3.0"
+
color@^3.0.0, color@^3.1.3:
version "3.2.0"
resolved "https://registry.yarnpkg.com/color/-/color-3.2.0.tgz#108509078b8b93746515cbdffb03e20097ec586b"
@@ -3998,6 +4031,16 @@ css-blank-pseudo@^0.1.4:
dependencies:
postcss "^7.0.5"
+css-color-function@^1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/css-color-function/-/css-color-function-1.3.3.tgz#8ed24c2c0205073339fafa004bc8c141fccb282e"
+ integrity sha1-jtJMLAIFBzM5+voAS8jBQfzLKC4=
+ dependencies:
+ balanced-match "0.1.0"
+ color "^0.11.0"
+ debug "^3.1.0"
+ rgb "~0.1.0"
+
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
@@ -8416,6 +8459,11 @@ pocket-js-core@0.0.3:
dependencies:
axios "^0.18.0"
+popper.js@^1.16.1:
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
+ integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
+
portfinder@^1.0.26:
version "1.0.28"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
@@ -9836,6 +9884,11 @@ rgb-regex@^1.0.1:
resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE=
+rgb@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5"
+ integrity sha1-vieykej+/+rBvZlylyG/pA/AN7U=
+
rgba-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
@@ -11323,6 +11376,16 @@ v-click-outside@^3.1.2:
resolved "https://registry.yarnpkg.com/v-click-outside/-/v-click-outside-3.1.2.tgz#1dbaa14bf09a21bd16f23a38f03bfa3988f71281"
integrity sha512-gMdRqfRE6m6XU6SiFi3dyBlFB2MWogiXpof8Aa3LQysrl9pzTndqp/iEaAphLoadaQUFnQ0ec6fLLaxr7LiY6A==
+v-tooltip@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/v-tooltip/-/v-tooltip-2.1.3.tgz#281c2015d1e73787f13c8956aa295b8c3a73f261"
+ integrity sha512-xXngyxLQTOx/yUEy50thb8te7Qo4XU6h4LZB6cvEfVd9mnysUxLEoYwGWDdqR+l69liKsy3IPkdYff3J1gAJ5w==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ lodash "^4.17.21"
+ popper.js "^1.16.1"
+ vue-resize "^1.0.1"
+
varint@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4"
@@ -11390,6 +11453,13 @@ vue-no-ssr@^1.1.1:
resolved "https://registry.yarnpkg.com/vue-no-ssr/-/vue-no-ssr-1.1.1.tgz#875f3be6fb0ae41568a837f3ac1a80eaa137b998"
integrity sha512-ZMjqRpWabMPqPc7gIrG0Nw6vRf1+itwf0Itft7LbMXs2g3Zs/NFmevjZGN1x7K3Q95GmIjWbQZTVerxiBxI+0g==
+vue-resize@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-1.0.1.tgz#c120bed4e09938771d622614f57dbcf58a5147ee"
+ integrity sha512-z5M7lJs0QluJnaoMFTIeGx6dIkYxOwHThlZDeQnWZBizKblb99GSejPnK37ZbNE/rVwDcYcHY+Io+AxdpY952w==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
vue-router@^3.5.1:
version "3.5.2"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.2.tgz#5f55e3f251970e36c3e8d88a7cd2d67a350ade5c"