mirror of
https://github.com/Instadapp/assembly.git
synced 2024-07-29 22:37:06 +00:00
commit
c52e2ac78c
5
assets/icons/currencies/lqty.svg
Normal file
5
assets/icons/currencies/lqty.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.6978 29.3646C21.2543 29.3646 27.3801 23.2388 27.3801 15.6823C27.3801 8.12578 21.2543 2 13.6978 2C6.14128 2 0.0155029 8.12578 0.0155029 15.6823C0.0155029 23.2388 6.14128 29.3646 13.6978 29.3646Z" fill="#2EB6EA"/>
|
||||
<path d="M13.6316 29.3645C0.0228213 29.3645 -5.34748 11.4618 6.67176 3.91699C5.24269 18.8796 12.846 19.2106 19.9028 18.8796C21.388 18.6893 22.8967 18.819 24.3277 19.2598C25.7586 19.7007 27.0788 20.4425 28.1994 21.4356C29.3201 22.4286 30.2153 23.6499 30.8251 25.0175C31.4349 26.385 31.7451 27.8672 31.7348 29.3645H13.6316Z" fill="#1542CD"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.6319 20.1584C25.598 22.8114 23.8056 25.1011 21.4784 26.7417C19.1513 28.3822 16.3924 29.3009 13.5463 29.3831C13.6867 29.3831 13.8276 29.3831 13.9691 29.3831H31.9604C31.9604 29.3831 31.957 22.9113 26.6319 20.1584Z" fill="#745DDF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 928 B |
15
assets/icons/currencies/lusd.svg
Normal file
15
assets/icons/currencies/lusd.svg
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<svg viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M300 600C465.685 600 600 465.685 600 300C600 134.315 465.685 0 300 0C134.315 0 0 134.315 0 300C0 465.685 134.315 600 300 600Z" fill="url(#paint0_radial)"/>
|
||||
<path d="M600 300C600 465.685 465.685 600 300 600L363 360.5L249 210L300 0C465.685 0 600 134.315 600 300Z" fill="url(#paint1_radial)"/>
|
||||
<path d="M340 597.357C326.915 599.1 313.562 600 300 600C287.128 600 274.445 599.189 262 597.616V477.694C212.011 460.846 183.312 409.537 176 375.156L242.885 355.534C247.249 373.668 260.951 400.568 283 410.5C300.714 418.479 326.652 413.303 336 400.5C356.984 371.762 349 340.5 270.426 308.441C210.153 283.849 199.607 245.651 199.607 214.256C199.607 188.709 212.632 142.374 262 123.883V2.38379C274.445 0.810688 287.128 0 300 0C313.562 0 326.915 0.899963 340 2.64343V120.602C380.767 132.965 401.957 167.283 408.131 186.783L356.984 222.103C344.5 178.5 286.164 169.529 270.426 197C251.759 229.584 287.294 244.077 318.112 256.646C322.004 258.234 325.82 259.79 329.443 261.348C363.5 276 409.521 307.913 414 363C418.26 415.393 400.455 461.225 340 477.384V597.357Z" fill="white"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(196 143.5) rotate(54.1675) scale(497.082)">
|
||||
<stop stop-color="#AFE9FF"/>
|
||||
<stop offset="1" stop-color="#2EB6EA"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint1_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(196 143.5) rotate(54.1675) scale(497.082)">
|
||||
<stop stop-color="#CFC5FF"/>
|
||||
<stop offset="1" stop-color="#745DDF"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
8
assets/icons/liquity.svg
Normal file
8
assets/icons/liquity.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<svg viewBox="0 0 31 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.8"
|
||||
d="M13.0413 25.9848C20.2316 25.9848 26.0605 20.1679 26.0605 12.9924C26.0605 5.81689 20.2316 0 13.0413 0C5.851 0 0.0220947 5.81689 0.0220947 12.9924C0.0220947 20.1679 5.851 25.9848 13.0413 25.9848Z"
|
||||
fill="currentColor" />
|
||||
<path opacity="0.8"
|
||||
d="M12.9703 25.9847C0.021819 25.9847 -5.08854 8.98532 6.34885 1.82129C4.98867 16.0293 12.2243 16.3435 18.9387 16.0293C30.1896 15.5026 30.1972 25.9847 30.1972 25.9847H12.9703Z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 579 B |
|
|
@ -35,7 +35,9 @@
|
|||
<SVGknc v-else-if="currency === 'knc'" class="h-full" />
|
||||
<SVGlend v-else-if="currency === 'lend'" class="h-full" />
|
||||
<SVGlink v-else-if="currency === 'link'" class="h-full" />
|
||||
<SVGlqty v-else-if="currency === 'lqty'" class="h-full" />
|
||||
<SVGlrc v-else-if="currency === 'lrc'" class="h-full" />
|
||||
<SVGlusd v-else-if="currency === 'lusd'" class="h-full" />
|
||||
<SVGmana v-else-if="currency === 'mana'" class="h-full" />
|
||||
<SVGmatic v-else-if="currency === 'matic'" class="h-full" />
|
||||
<SVGmkr v-else-if="currency === 'mkr'" class="h-full" />
|
||||
|
|
@ -157,6 +159,9 @@ import SVGyfi from '@/assets/icons/currencies/yfi.svg?inline'
|
|||
import SVGzil from '@/assets/icons/currencies/zil.svg?inline'
|
||||
import SVGzrx from '@/assets/icons/currencies/zrx.svg?inline'
|
||||
import SVGCustom from '@/assets/icons/currencies/custom.svg?inline'
|
||||
import SVGlqty from '@/assets/icons/currencies/lqty.svg?inline'
|
||||
import SVGlusd from '@/assets/icons/currencies/lusd.svg?inline'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
currency: { type: String, default: '' },
|
||||
|
|
@ -232,6 +237,8 @@ export default defineComponent({
|
|||
SVGyfi,
|
||||
SVGzil,
|
||||
SVGzrx,
|
||||
SVGlqty,
|
||||
SVGlusd,
|
||||
SVGCustom,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
29
components/common/input/ButtonDashed.vue
Normal file
29
components/common/input/ButtonDashed.vue
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<button
|
||||
class="flex items-center justify-center px-4 font-semibold leading-none transition-colors duration-150 border border-dashed cursor-pointer focus:outline-none rounded-[4px]"
|
||||
:class="{
|
||||
'w-full': fullWidth,
|
||||
'text-ocean-blue-pure dark:text-white border-ocean-blue-pure dark:border-grey-pure bg-ocean-blue-light border-opacity-25 bg-opacity-38 dark:bg-opacity-17 dark:hover:bg-opacity-25 hover:bg-opacity-75 active:bg-opacity-38 dark:active:bg-opacity-38 ':
|
||||
color === 'ocean-blue',
|
||||
}"
|
||||
:style="`height: ${height}`"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<slot></slot>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from '@nuxtjs/composition-api'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
color: { type: String, default: 'grey' },
|
||||
height: { type: String, default: '60px' },
|
||||
fullWidth: { type: Boolean, default: false },
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
155
components/common/input/InputAmount.vue
Normal file
155
components/common/input/InputAmount.vue
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<template>
|
||||
<div class="flex flex-col flex-shrink-0 w-full">
|
||||
<div class="relative transition-all duration-150 rounded-sm" :class="{ 'shadow-sm': !disabled }">
|
||||
<input
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
inputmode="decimal"
|
||||
:value="value"
|
||||
v-bind="$attrs"
|
||||
class="w-full pl-4 rounded-[6px] transition-colors duration-75 ease-out border border-grey-dark border-opacity-[0.15]"
|
||||
:class="{
|
||||
'pr-12': !!symbol,
|
||||
'pr-8': !symbol,
|
||||
'rounded-b-none': badgeValue != null,
|
||||
'text-sm': size === 'lg',
|
||||
'bg-gray-50': backgroundColor === 'grey',
|
||||
}"
|
||||
:aria-invalid="(touched || showError) && !!error"
|
||||
:aria-describedby="id"
|
||||
:disabled="disabled"
|
||||
v-on="inputListeners"
|
||||
/>
|
||||
|
||||
<div v-if="!!tokenKeys && !!tokenKeys.length" class="absolute inset-y-0 right-0">
|
||||
<Dropdown class="h-full">
|
||||
<template #trigger="{ toggle }">
|
||||
<button
|
||||
v-if="!!symbol"
|
||||
class="flex items-center h-full px-1 font-semibold whitespace-no-wrap text-ocean-blue-pure text-11 dark:text-light"
|
||||
@click="toggle"
|
||||
>
|
||||
<span class="mr-1">{{ symbol }}</span>
|
||||
<Icon name="chevron-down" class="h-3" />
|
||||
</button>
|
||||
</template>
|
||||
<template #menu="{ close }">
|
||||
<DropdownMenu>
|
||||
<button
|
||||
v-for="(tokenKey, index) in tokenKeys"
|
||||
:key="index"
|
||||
class="flex items-center w-full px-4 py-2 font-semibold text-ocean-blue-pure text-11 dark:text-light hover:bg-opacity-50 focus:bg-opacity-50 hover:bg-grey-light dark:hover:bg-dark-300 dark:focus:bg-dark-300 focus:outline-none focus:bg-grey-light"
|
||||
@click="select(tokenKey, close)"
|
||||
>
|
||||
{{ getSymbol(tokenKey) }}
|
||||
</button>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="!!getSymbol(tokenKey)"
|
||||
class="absolute inset-y-0 right-0 flex items-center pr-4 pointer-events-none select-none"
|
||||
>
|
||||
<span
|
||||
class="uppercase"
|
||||
:class="{
|
||||
'text-ocean-blue-pure dark:text-light': !disabled,
|
||||
'text-grey-pure': disabled,
|
||||
'text-11': size === 'md',
|
||||
'text-14 font-medium': size === 'lg',
|
||||
}"
|
||||
>
|
||||
{{ getSymbol(tokenKey) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Badge
|
||||
v-if="badgeValue != null"
|
||||
class="h-6 rounded-t-none rounded-b-sm bg-opacity-38 bg-grey-pure text-opacity-38"
|
||||
large
|
||||
>
|
||||
<SVGSpinner v-if="badgeLoading" class="animate-spin-loading" style="width: auto; height: 1em" />
|
||||
<span v-else>{{ badgeValue }}</span>
|
||||
</Badge>
|
||||
|
||||
<div class="h-4">
|
||||
<transition
|
||||
enter-active-class="duration-75 ease-out"
|
||||
enter-class="opacity-0"
|
||||
enter-to-class="opacity-100"
|
||||
leave-active-class="duration-75 ease-in"
|
||||
leave-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<p v-if="(touched || showError) && !!error" :id="id" class="mt-1 text-red-600 text-12">
|
||||
{{ error }}
|
||||
</p>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, watch, ref, toRef } from '@nuxtjs/composition-api'
|
||||
import { useInputListeners } from '@/composables/useInputListeners'
|
||||
import SVGSpinner from '@/assets/img/icons/spinner.svg'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import { useToken } from '~/composables/useToken'
|
||||
import { useFormatting } from '~/composables/useFormatting'
|
||||
import { usePattern } from '~/composables/usePattern'
|
||||
import Dropdown from './Dropdown.vue'
|
||||
import DropdownMenu from '~/components/protocols/DropdownMenu.vue'
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
components: {
|
||||
SVGSpinner,
|
||||
Dropdown,
|
||||
DropdownMenu,
|
||||
},
|
||||
props: {
|
||||
value: { type: String, default: '' },
|
||||
badgeValue: { type: String, default: null },
|
||||
badgeLoading: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
showError: { type: Boolean, default: false },
|
||||
error: { type: String, default: null },
|
||||
tokenKey: { type: String, required: true },
|
||||
tokenKeys: { type: Array, default: () => null },
|
||||
size: { type: String, default: 'md' },
|
||||
backgroundColor: { type: String, default: 'white' },
|
||||
},
|
||||
|
||||
setup(props, context) {
|
||||
const id = uuid()
|
||||
const { symbol, getTokenByKey } = useToken(null, toRef(props, 'tokenKey'))
|
||||
const { formatDecimal } = useFormatting()
|
||||
const { amountPattern } = usePattern()
|
||||
|
||||
const numericFilter = (value) => amountPattern.test(value)
|
||||
const { inputListeners } = useInputListeners(props, context, numericFilter)
|
||||
|
||||
const touched = ref(false)
|
||||
const stopTouchedWatcher = watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
touched.value = true
|
||||
stopTouchedWatcher()
|
||||
}
|
||||
)
|
||||
|
||||
function getSymbol(key) {
|
||||
return getTokenByKey(key)?.symbol || key
|
||||
}
|
||||
|
||||
function select(tokenKey, cb) {
|
||||
context.emit('tokenKeyChanged', tokenKey)
|
||||
cb()
|
||||
}
|
||||
|
||||
return { inputListeners, touched, symbol, formatDecimal, id, getSymbol, select }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
120
components/protocols/liquity/CardLiquityTrove.vue
Normal file
120
components/protocols/liquity/CardLiquityTrove.vue
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<div
|
||||
class="flex-shrink-0 bg-white rounded-lg relative flex flex-col flex-1 px-4 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">
|
||||
<IconCurrency :currency="token.key" class="w-12 h-12" no-height />
|
||||
<div class="flex flex-col flex-grow mx-4">
|
||||
<div class="mb-1 font-medium leading-none whitespace-no-wrap text-19">
|
||||
{{ formatUsd(amountUsd) }}
|
||||
</div>
|
||||
<div class="flex leading-none whitespace-no-wrap">
|
||||
<span class="text-grey-pure text-14"
|
||||
>{{ formatDecimal(amount) }} {{ token.symbol }}</span
|
||||
>
|
||||
<Info
|
||||
:text="`${formatUsd(priceInUsd, 2)}/${token.symbol}`"
|
||||
icon="price"
|
||||
class="ml-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="self-start">
|
||||
<Badge
|
||||
:color="positionType === 'supply' ? 'green' : 'yellow'"
|
||||
class="w-16"
|
||||
>{{ badge }}</Badge
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mt-4" />
|
||||
|
||||
<div class="flex items-center justify-around px-4 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="supplyOrBorrow"
|
||||
>
|
||||
{{ buttonOne }}
|
||||
</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="withdrawOrPayback"
|
||||
>
|
||||
{{ buttonTwo }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, defineComponent, useContext } from '@nuxtjs/composition-api'
|
||||
import { useFormatting } from '@/composables/useFormatting'
|
||||
import { useBigNumber } from '~/composables/useBigNumber'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
collateral: { type: String, default: '0' },
|
||||
debt: { type: String, default: '0' },
|
||||
amountUsd: { type: String, default: '0' },
|
||||
positionType: { type: String, default: 'no' },
|
||||
token: { type: Object, default: () => { } },
|
||||
priceInUsd: { type: String, default: '0' },
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const { app } = useContext()
|
||||
const { formatUsd, formatDecimal } = useFormatting()
|
||||
const { isZero } = useBigNumber()
|
||||
|
||||
const amount = computed(() => (props.positionType === 'supply' ? props.collateral : props.debt))
|
||||
const withdrawOrPaybackDisabled = computed(() => isZero(amount.value))
|
||||
|
||||
const buttonOne = computed(() => (props.positionType === 'supply' ? 'Supply' : 'Borrow'))
|
||||
const buttonTwo = computed(() => (props.positionType === 'supply' ? 'Withdraw' : 'Payback'))
|
||||
const badge = computed(() => (props.positionType === 'supply' ? 'Collateral' : 'Debt'))
|
||||
|
||||
function showSupply() {
|
||||
app.router.push({ hash: 'trove-supply' })
|
||||
}
|
||||
|
||||
function showBorrow() {
|
||||
if (isZero(props.collateral)) return
|
||||
|
||||
app.router.push({ hash: 'trove-borrow' })
|
||||
}
|
||||
|
||||
function showPayback() {
|
||||
if (isZero(amount.value)) return
|
||||
|
||||
app.router.push({ hash: 'trove-payback' })
|
||||
}
|
||||
|
||||
function showWithdraw() {
|
||||
if (isZero(amount.value)) return
|
||||
|
||||
app.router.push({ hash: 'trove-withdraw' })
|
||||
}
|
||||
|
||||
function supplyOrBorrow() {
|
||||
props.positionType === 'supply' ? showSupply() : showBorrow()
|
||||
}
|
||||
function withdrawOrPayback() {
|
||||
props.positionType === 'supply' ? showWithdraw() : showPayback()
|
||||
}
|
||||
|
||||
return {
|
||||
formatUsd,
|
||||
formatDecimal,
|
||||
buttonOne,
|
||||
buttonTwo,
|
||||
badge,
|
||||
supplyOrBorrow,
|
||||
withdrawOrPayback,
|
||||
withdrawOrPaybackDisabled,
|
||||
amount,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
225
components/sidebar/context/liquity/SidebarLiquityTroveBorrow.vue
Normal file
225
components/sidebar/context/liquity/SidebarLiquityTroveBorrow.vue
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
<template>
|
||||
<SidebarContextRootContainer>
|
||||
<template #title>Borrow {{ debtToken.symbol }}</template>
|
||||
|
||||
<div class="flex justify-around items-center w-full">
|
||||
<SidebarSectionValueWithIcon class="" label="Debt" center>
|
||||
<template #icon
|
||||
><IconCurrency :currency="debtToken.key" class="w-16 h-16" noHeight
|
||||
/></template>
|
||||
<template #value
|
||||
>{{ formatDecimal(changedDebt) }} {{ debtToken.symbol }}</template
|
||||
>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<SidebarSectionValueWithIcon class="" label="Token Balance" center>
|
||||
<template #icon
|
||||
><IconCurrency :currency="debtToken.key" class="w-16 h-16" noHeight
|
||||
/></template>
|
||||
|
||||
<template #value
|
||||
>{{ formatDecimal(changedBalance) }} {{ debtToken.symbol }}</template
|
||||
>
|
||||
</SidebarSectionValueWithIcon>
|
||||
</div>
|
||||
|
||||
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8">
|
||||
<input-amount
|
||||
v-model="amount"
|
||||
:token-key="debtToken.key"
|
||||
:disabled="pending"
|
||||
class="mt-4"
|
||||
placeholder="Amount to borrow"
|
||||
:error="errors.amount.message"
|
||||
/>
|
||||
|
||||
<ValueDisplay
|
||||
label="Borrow Fee"
|
||||
tooltip="This amount is deducted from the borrowed amount as a one-time fee. There are no recurring fees for borrowing, which is thus interest-free."
|
||||
class="mt-4"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
{{ formatDecimal(borrowFeeAmount, 2) }} {{ debtToken.symbol }}
|
||||
</div>
|
||||
<div class="ml-1 text-sm">({{ formatPercent(borrowFee) }})</div>
|
||||
</div>
|
||||
</ValueDisplay>
|
||||
<ValueDisplay label="Debt with fee" class="mt-4">
|
||||
<div>
|
||||
{{ formatDecimal(inputAmountWithFee, 2) }} {{ debtToken.symbol }}
|
||||
</div>
|
||||
</ValueDisplay>
|
||||
|
||||
<SidebarContextHeading class="mt-5">
|
||||
Projected Debt Position
|
||||
</SidebarContextHeading>
|
||||
|
||||
<SidebarSectionStatus
|
||||
class="mt-8"
|
||||
:liquidation="liquidation"
|
||||
:status="status"
|
||||
/>
|
||||
|
||||
<SidebarSectionValueWithIcon class="mt-8" label="Liquidation Price (ETH)">
|
||||
<template #value>
|
||||
{{ formatUsdMax(liquidationPrice, liquidationMaxPrice) }}
|
||||
<span class="text-primary-gray"
|
||||
>/ {{ formatUsd(liquidationMaxPrice) }}</span
|
||||
>
|
||||
</template>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<div class="flex flex-shrink-0 mt-10">
|
||||
<ButtonCTA
|
||||
class="w-full"
|
||||
:disabled="!isValid || pending"
|
||||
:loading="pending"
|
||||
@click="cast"
|
||||
>
|
||||
Borrow
|
||||
</ButtonCTA>
|
||||
</div>
|
||||
|
||||
<ValidationErrors :error-messages="errorMessages" class="mt-6" />
|
||||
</div>
|
||||
</SidebarContextRootContainer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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 { useWeb3 } from '~/composables/useWeb3'
|
||||
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 { useLiquityPosition } from '~/composables/protocols/useLiquityPosition'
|
||||
import InputAmount from '~/components/common/input/InputAmount.vue'
|
||||
import ValueDisplay from '../components/ValueDisplay.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { InputNumeric, ToggleButton, ButtonCTA, Button, InputAmount, ValueDisplay },
|
||||
setup() {
|
||||
const { account } = useWeb3()
|
||||
const { close } = useSidebar()
|
||||
const { formatPercent, formatNumber, formatDecimal, formatUsdMax, formatUsd } = useFormatting()
|
||||
const { plus, times, isZero, max, min } = useBigNumber()
|
||||
const { parseSafeFloat } = useParsing()
|
||||
const { getBalanceByKey } = useBalances()
|
||||
const { valInt } = useToken()
|
||||
const { showPendingTransaction, showWarning } = useNotification()
|
||||
const { dsa } = useDSA()
|
||||
|
||||
const amount = ref('')
|
||||
const amountParsed = computed(() => parseSafeFloat(amount.value))
|
||||
|
||||
const {
|
||||
debt,
|
||||
collateral,
|
||||
collateralInWei,
|
||||
liquidation,
|
||||
liquidationMaxPrice,
|
||||
debtToken,
|
||||
collateralToken,
|
||||
maxFeePercentageInWei,
|
||||
getTrovePositionHints,
|
||||
borrowFee,
|
||||
} = useLiquityPosition()
|
||||
|
||||
const balance = computed(() => getBalanceByKey(debtToken.value.key))
|
||||
|
||||
const changedBalance = computed(() => max(plus(balance.value, amountParsed.value), '0').toFixed())
|
||||
const changedDebt = computed(() => max(plus(debt.value, inputAmountWithFee.value), '0').toFixed())
|
||||
const { liquidationPrice, status } = useLiquityPosition(collateral, changedDebt)
|
||||
|
||||
const borrowFeeAmount = computed(() => times(amountParsed.value, borrowFee.value).toFixed())
|
||||
const inputAmountWithFee = computed(() => plus(amountParsed.value, borrowFeeAmount.value).toFixed())
|
||||
|
||||
const { validateAmount, validateLiquidation, validateIsLoggedIn, validateLiquityTroveExists } = useValidators()
|
||||
|
||||
const errors = computed(() => {
|
||||
const hasAmountValue = !isZero(amount.value)
|
||||
|
||||
return {
|
||||
troveExists: { message: validateLiquityTroveExists(), show: true },
|
||||
amount: { message: validateAmount(amountParsed.value), show: hasAmountValue },
|
||||
liquidation: { message: validateLiquidation(status.value, liquidation.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
|
||||
try {
|
||||
const inputAmountInWei = valInt(amountParsed.value, debtToken.value.decimals)
|
||||
const totalDebtAmountInWei = valInt(changedDebt.value, debtToken.value.decimals)
|
||||
|
||||
const { upperHint, lowerHint } = await getTrovePositionHints(collateralInWei.value, totalDebtAmountInWei)
|
||||
|
||||
const spells = dsa.value.Spell()
|
||||
|
||||
const getId = 0
|
||||
const setId = 0
|
||||
|
||||
|
||||
spells.add({
|
||||
connector: 'LIQUITY-A',
|
||||
method: 'borrow',
|
||||
args: [maxFeePercentageInWei.value, inputAmountInWei, upperHint, lowerHint, getId, setId],
|
||||
})
|
||||
|
||||
const txHash = await dsa.value.cast({
|
||||
spells,
|
||||
from: account.value,
|
||||
})
|
||||
|
||||
showPendingTransaction(txHash)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
showWarning(error.message)
|
||||
}
|
||||
|
||||
pending.value = false
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
return {
|
||||
amount,
|
||||
status,
|
||||
liquidation,
|
||||
liquidationPrice,
|
||||
liquidationMaxPrice,
|
||||
formatUsd,
|
||||
formatUsdMax,
|
||||
formatDecimal,
|
||||
formatPercent,
|
||||
errors,
|
||||
errorMessages,
|
||||
isValid,
|
||||
cast,
|
||||
pending,
|
||||
debtToken,
|
||||
collateralToken,
|
||||
borrowFee,
|
||||
borrowFeeAmount,
|
||||
inputAmountWithFee,
|
||||
changedDebt,
|
||||
changedBalance,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
<template>
|
||||
<SidebarContextRootContainer>
|
||||
<template #title>Open Trove</template>
|
||||
|
||||
<SidebarSectionValueWithIcon label="Collateral Balance" center>
|
||||
<template #icon
|
||||
><IconCurrency
|
||||
:currency="collateralToken.key"
|
||||
class="w-16 h-16"
|
||||
noHeight
|
||||
/></template>
|
||||
<template #value
|
||||
>{{ formatNumber(balance) }} {{ collateralToken.symbol }}</template
|
||||
>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8">
|
||||
<input-amount
|
||||
v-model="collateralAmount"
|
||||
:token-key="collateralToken.key"
|
||||
:disabled="pending"
|
||||
class="mt-4"
|
||||
placeholder="Collateral amount"
|
||||
:error="errors.collateralAmount.message"
|
||||
/>
|
||||
|
||||
<input-amount
|
||||
v-model="debtAmount"
|
||||
:token-key="debtToken.key"
|
||||
:disabled="pending"
|
||||
class="mt-4"
|
||||
placeholder="Borrow amount"
|
||||
:error="errors.debtAmount.message"
|
||||
/>
|
||||
|
||||
<ValueDisplay
|
||||
label="Liquidation Reserve"
|
||||
tooltip="An amount set aside to cover the liquidator’s gas costs if your Trove needs to be liquidated. The amount increases your debt and is refunded if you close your Trove by fully paying off its net debt."
|
||||
class="mt-4"
|
||||
>
|
||||
{{ liquidationReserve }} LUSD
|
||||
</ValueDisplay>
|
||||
<ValueDisplay
|
||||
label="Borrow Fee"
|
||||
tooltip="This amount is deducted from the borrowed amount as a one-time fee. There are no recurring fees for borrowing, which is thus interest-free."
|
||||
class="mt-4"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
{{ formatDecimal(borrowFeeAmount, 2) }} {{ debtToken.symbol }}
|
||||
</div>
|
||||
<div class="ml-1 text-sm">({{ formatPercent(borrowFee) }})</div>
|
||||
</div>
|
||||
</ValueDisplay>
|
||||
<ValueDisplay
|
||||
label="Total debt"
|
||||
tooltip="The total amount of LUSD your Trove will hold."
|
||||
class="mt-4"
|
||||
>
|
||||
<div>{{ formatDecimal(totalDebt, 2) }} {{ debtToken.symbol }}</div>
|
||||
</ValueDisplay>
|
||||
|
||||
<SidebarContextHeading class="mt-5">
|
||||
Projected Debt Position
|
||||
</SidebarContextHeading>
|
||||
|
||||
<SidebarSectionStatus
|
||||
class="mt-8"
|
||||
:liquidation="liquidation"
|
||||
:status="status"
|
||||
/>
|
||||
|
||||
<SidebarSectionValueWithIcon class="mt-8" label="Liquidation Price (ETH)">
|
||||
<template #value>
|
||||
{{ formatUsdMax(liquidationPrice, liquidationMaxPrice) }}
|
||||
<span class="text-primary-gray"
|
||||
>/ {{ formatUsd(liquidationMaxPrice) }}</span
|
||||
>
|
||||
</template>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<div class="flex flex-shrink-0 mt-10">
|
||||
<ButtonCTA
|
||||
class="w-full"
|
||||
:disabled="!isValid || pending"
|
||||
:loading="pending"
|
||||
@click="cast"
|
||||
>
|
||||
Open Trove
|
||||
</ButtonCTA>
|
||||
</div>
|
||||
|
||||
<ValidationErrors :error-messages="errorMessages" class="mt-6" />
|
||||
</div>
|
||||
</SidebarContextRootContainer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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 '~/composables/useWeb3'
|
||||
import atokens from '~/constant/atokens'
|
||||
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 { useLiquityPosition } from '~/composables/protocols/useLiquityPosition'
|
||||
import InputAmount from '~/components/common/input/InputAmount.vue'
|
||||
import ValueDisplay from '../components/ValueDisplay.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { InputNumeric, ToggleButton, ButtonCTA, Button, InputAmount, ValueDisplay },
|
||||
setup() {
|
||||
const { account } = useWeb3()
|
||||
const { close } = useSidebar()
|
||||
const { formatPercent, formatNumber, formatDecimal, formatUsdMax, formatUsd } = useFormatting()
|
||||
const { plus, times, isZero } = useBigNumber()
|
||||
const { parseSafeFloat } = useParsing()
|
||||
const { getBalanceByKey } = useBalances()
|
||||
const { valInt } = useToken()
|
||||
const { showPendingTransaction, showWarning } = useNotification()
|
||||
const { dsa } = useDSA()
|
||||
|
||||
const {
|
||||
collateralToken,
|
||||
debtToken,
|
||||
liquidation,
|
||||
liquidationReserve,
|
||||
liquidationMaxPrice,
|
||||
borrowFee,
|
||||
maxFeePercentageInWei,
|
||||
getTrovePositionHints,
|
||||
} = useLiquityPosition()
|
||||
|
||||
const collateralAmount = ref('')
|
||||
const debtAmount = ref('')
|
||||
|
||||
|
||||
const balance = computed(() => getBalanceByKey(collateralToken.value.key))
|
||||
|
||||
const collateralAmountParsed = computed(() => parseSafeFloat(collateralAmount.value))
|
||||
const debtAmountParsed = computed(() => parseSafeFloat(debtAmount.value))
|
||||
|
||||
const borrowFeeAmount = computed(() => times(debtAmountParsed.value, borrowFee.value).toFixed())
|
||||
const totalDebt = computed(() => {
|
||||
if (isZero(debtAmountParsed.value)) return '0'
|
||||
return plus(plus(debtAmountParsed.value, borrowFeeAmount.value), liquidationReserve.value).toFixed()
|
||||
})
|
||||
|
||||
const { liquidationPrice, status } = useLiquityPosition(collateralAmountParsed, totalDebt)
|
||||
|
||||
const { validateAmount, validateLiquidation, validateIsLoggedIn, validateLiquityDebt } = useValidators()
|
||||
|
||||
const errors = computed(() => {
|
||||
const hasCollateralAmountValue = !isZero(collateralAmount.value)
|
||||
const hasDebtAmountValue = !isZero(debtAmount.value)
|
||||
|
||||
return {
|
||||
collateralAmount: {
|
||||
message: validateAmount(collateralAmountParsed.value, balance.value),
|
||||
show: hasCollateralAmountValue,
|
||||
},
|
||||
debtAmount: { message: validateAmount(debtAmountParsed.value), show: hasDebtAmountValue },
|
||||
minDebt: { message: validateLiquityDebt(totalDebt.value, undefined, '0'), show: hasDebtAmountValue },
|
||||
liquidation: {
|
||||
message: validateLiquidation(status.value, liquidation.value),
|
||||
show: hasCollateralAmountValue && hasDebtAmountValue,
|
||||
},
|
||||
auth: { message: validateIsLoggedIn(!!account.value), show: true },
|
||||
}
|
||||
})
|
||||
const { errorMessages, isValid } = useValidation(errors)
|
||||
|
||||
const pending = ref(false)
|
||||
|
||||
async function cast() {
|
||||
pending.value = true
|
||||
try {
|
||||
const depositAmountInWei = valInt(collateralAmountParsed.value, collateralToken.value.decimals)
|
||||
const borrowAmountInWei = valInt(debtAmountParsed.value, debtToken.value.decimals)
|
||||
const totalBorrowAmountInWei = valInt(totalDebt.value, debtToken.value.decimals)
|
||||
|
||||
const { upperHint, lowerHint } = await getTrovePositionHints(depositAmountInWei, totalBorrowAmountInWei)
|
||||
|
||||
const spells = dsa.value.Spell()
|
||||
|
||||
const getIds = [0, 0]
|
||||
const setIds = [0, 0]
|
||||
|
||||
spells.add({
|
||||
connector: 'LIQUITY-A',
|
||||
method: 'open',
|
||||
args: [
|
||||
depositAmountInWei,
|
||||
maxFeePercentageInWei.value,
|
||||
borrowAmountInWei,
|
||||
upperHint,
|
||||
lowerHint,
|
||||
getIds,
|
||||
setIds,
|
||||
],
|
||||
})
|
||||
|
||||
const txHash = await dsa.value.cast({
|
||||
spells,
|
||||
from: account.value,
|
||||
})
|
||||
|
||||
showPendingTransaction(txHash)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
showWarning(error.message)
|
||||
}
|
||||
|
||||
pending.value = false
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
return {
|
||||
formatPercent, formatNumber, formatDecimal, formatUsdMax, formatUsd,
|
||||
balance,
|
||||
liquidationPrice,
|
||||
liquidationMaxPrice,
|
||||
status,
|
||||
liquidation,
|
||||
totalDebt,
|
||||
liquidationReserve,
|
||||
collateralToken,
|
||||
debtToken,
|
||||
borrowFee,
|
||||
borrowFeeAmount,
|
||||
collateralAmount,
|
||||
debtAmount,
|
||||
errors,
|
||||
errorMessages,
|
||||
isValid,
|
||||
pending,
|
||||
cast,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
<template>
|
||||
<SidebarContextRootContainer>
|
||||
<template #title>Payback {{ debtToken.symbol }}</template>
|
||||
|
||||
<div class="flex justify-around items-center w-full">
|
||||
<SidebarSectionValueWithIcon class="" label="Debt" center>
|
||||
<template #icon
|
||||
><IconCurrency :currency="debtToken.key" class="w-16 h-16" noHeight
|
||||
/></template>
|
||||
<template #value>{{ formatDecimal(changedDebt) }} {{ debtToken.symbol }}</template>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<SidebarSectionValueWithIcon class="" label="Token Balance" center>
|
||||
<template #icon
|
||||
><IconCurrency :currency="debtToken.key" class="w-16 h-16" noHeight
|
||||
/></template>
|
||||
|
||||
<template #value>{{ formatDecimal(changedBalance) }} {{ debtToken.symbol }}</template>
|
||||
</SidebarSectionValueWithIcon>
|
||||
</div>
|
||||
|
||||
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8">
|
||||
<h3 class="text-primary-gray text-xs font-semibold mb-2.5">
|
||||
Amount to payback
|
||||
</h3>
|
||||
|
||||
<input-numeric
|
||||
v-model="amount"
|
||||
placeholder="Amount to payback"
|
||||
: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>
|
||||
|
||||
<SidebarContextHeading class="mt-5">
|
||||
Projected Debt Position
|
||||
</SidebarContextHeading>
|
||||
|
||||
<SidebarSectionStatus
|
||||
class="mt-8"
|
||||
:liquidation="liquidation"
|
||||
:status="status"
|
||||
/>
|
||||
|
||||
<SidebarSectionValueWithIcon
|
||||
class="mt-8"
|
||||
:label="`Liquidation Price (${collateralToken.symbol})`"
|
||||
>
|
||||
<template #value>
|
||||
{{ formatUsdMax(liquidationPrice, liquidationMaxPrice) }}
|
||||
<span class="text-primary-gray"
|
||||
>/ {{ formatUsd(liquidationMaxPrice) }}</span
|
||||
>
|
||||
</template>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<div class="flex flex-shrink-0 mt-10">
|
||||
<ButtonCTA
|
||||
class="w-full"
|
||||
:disabled="!isValid || pending"
|
||||
:loading="pending"
|
||||
@click="cast"
|
||||
>
|
||||
Payback
|
||||
</ButtonCTA>
|
||||
</div>
|
||||
|
||||
<ValidationErrors :error-messages="errorMessages" class="mt-6" />
|
||||
</div>
|
||||
</SidebarContextRootContainer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, defineComponent, 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 '~/composables/useWeb3'
|
||||
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 { useLiquityPosition } from '~/composables/protocols/useLiquityPosition'
|
||||
|
||||
export default defineComponent({
|
||||
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
|
||||
setup() {
|
||||
const { close } = useSidebar()
|
||||
const { networkName, account } = useWeb3()
|
||||
const { dsa } = useDSA()
|
||||
const { valInt } = useToken()
|
||||
const { getBalanceByKey } = useBalances()
|
||||
const { formatDecimal, formatUsdMax, formatUsd } = useFormatting()
|
||||
const { isZero, gte, plus, max, minus, min } = useBigNumber()
|
||||
const { parseSafeFloat } = useParsing()
|
||||
const { showPendingTransaction, showWarning } = useNotification()
|
||||
|
||||
const amount = ref('')
|
||||
const amountParsed = computed(() => parseSafeFloat(amount.value))
|
||||
|
||||
const {
|
||||
debt,
|
||||
collateral,
|
||||
collateralInWei,
|
||||
liquidation,
|
||||
liquidationMaxPrice,
|
||||
debtToken,
|
||||
collateralToken,
|
||||
getTrovePositionHints,
|
||||
} = useLiquityPosition()
|
||||
|
||||
const balance = computed(() => getBalanceByKey(debtToken.value.key))
|
||||
|
||||
const changedDebt = computed(() => max(minus(debt.value, amountParsed.value), '0').toFixed())
|
||||
const changedBalance = computed(() => max(minus(balance.value, amountParsed.value), '0').toFixed())
|
||||
|
||||
const { liquidationPrice, status } = useLiquityPosition(collateral, changedDebt)
|
||||
const { validateAmount, validateIsLoggedIn, validateLiquityDebt, validateLiquityTroveExists } = useValidators()
|
||||
|
||||
const maxBalance = computed(() => min(balance.value, debt.value).toFixed())
|
||||
const { toggle, isMaxAmount } = useMaxAmountActive(amount, maxBalance)
|
||||
|
||||
const errors = computed(() => {
|
||||
const hasAmountValue = !isZero(amount.value)
|
||||
|
||||
return {
|
||||
troveExists: { message: validateLiquityTroveExists(), show: true },
|
||||
amount: { message: validateAmount(amountParsed.value, maxBalance.value), show: hasAmountValue },
|
||||
minDebt: { message: validateLiquityDebt(changedDebt.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 inputAmountInWei = valInt(amountParsed.value, debtToken.value.decimals)
|
||||
const totalDebtAmountInWei = valInt(changedDebt.value, debtToken.value.decimals)
|
||||
const { upperHint, lowerHint } = await getTrovePositionHints(collateralInWei.value, totalDebtAmountInWei)
|
||||
|
||||
const getId = 0
|
||||
const setId = 0
|
||||
|
||||
const spells = dsa.value.Spell()
|
||||
|
||||
spells.add({
|
||||
connector: 'LIQUITY-A',
|
||||
method: 'repay',
|
||||
args: [inputAmountInWei, upperHint, lowerHint, getId, setId],
|
||||
})
|
||||
|
||||
|
||||
try {
|
||||
const txHash = await dsa.value.cast({
|
||||
spells,
|
||||
from: account.value,
|
||||
})
|
||||
|
||||
showPendingTransaction(txHash)
|
||||
} catch (error) {
|
||||
showWarning(error.message)
|
||||
}
|
||||
|
||||
pending.value = false
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
return {
|
||||
debt,
|
||||
balance,
|
||||
amount,
|
||||
status,
|
||||
liquidation,
|
||||
liquidationPrice,
|
||||
liquidationMaxPrice,
|
||||
formatUsd,
|
||||
formatUsdMax,
|
||||
formatDecimal,
|
||||
errors,
|
||||
errorMessages,
|
||||
isMaxAmount,
|
||||
isValid,
|
||||
cast,
|
||||
pending,
|
||||
toggle,
|
||||
debtToken,
|
||||
collateralToken,
|
||||
changedDebt,
|
||||
changedBalance,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
205
components/sidebar/context/liquity/SidebarLiquityTroveSupply.vue
Normal file
205
components/sidebar/context/liquity/SidebarLiquityTroveSupply.vue
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
<template>
|
||||
<SidebarContextRootContainer>
|
||||
<template #title>Supply {{ collateralToken.symbol }}</template>
|
||||
|
||||
<SidebarSectionValueWithIcon label="Token Balance" center>
|
||||
<template #icon
|
||||
><IconCurrency
|
||||
:currency="collateralToken.key"
|
||||
class="w-20 h-20"
|
||||
noHeight
|
||||
/></template>
|
||||
<template #value
|
||||
>{{ formatDecimal(balance) }} {{ collateralToken.symbol }}</template
|
||||
>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8">
|
||||
<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>
|
||||
|
||||
<SidebarContextHeading class="mt-5">
|
||||
Projected Debt Position
|
||||
</SidebarContextHeading>
|
||||
|
||||
<SidebarSectionStatus
|
||||
class="mt-8"
|
||||
:liquidation="liquidation"
|
||||
:status="status"
|
||||
/>
|
||||
|
||||
<SidebarSectionValueWithIcon class="mt-8" label="Liquidation Price (ETH)">
|
||||
<template #value>
|
||||
{{ formatUsdMax(liquidationPrice, liquidationMaxPrice) }}
|
||||
<span class="text-primary-gray"
|
||||
>/ {{ formatUsd(liquidationMaxPrice) }}</span
|
||||
>
|
||||
</template>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<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>
|
||||
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 '~/composables/useWeb3'
|
||||
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 { useLiquityPosition } from '~/composables/protocols/useLiquityPosition'
|
||||
|
||||
export default defineComponent({
|
||||
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
|
||||
setup() {
|
||||
const { close } = useSidebar()
|
||||
const { account } = useWeb3()
|
||||
const { dsa } = useDSA()
|
||||
const { valInt } = useToken()
|
||||
const { getBalanceByKey } = useBalances()
|
||||
const { formatDecimal, formatUsdMax, formatUsd } = useFormatting()
|
||||
const { isZero, gt, plus, max, minus } = useBigNumber()
|
||||
const { parseSafeFloat } = useParsing()
|
||||
const { showPendingTransaction, showWarning } = useNotification()
|
||||
|
||||
const amount = ref('')
|
||||
const amountParsed = computed(() => parseSafeFloat(amount.value))
|
||||
|
||||
const {
|
||||
collateralToken,
|
||||
debt,
|
||||
debtInWei,
|
||||
collateral,
|
||||
collateralInWei,
|
||||
liquidation,
|
||||
liquidationMaxPrice,
|
||||
getTrovePositionHints,
|
||||
} = useLiquityPosition()
|
||||
|
||||
const changedCollateral = computed(() => max(plus(collateral.value, amountParsed.value), '0').toFixed())
|
||||
const changedBalance = computed(() => max(minus(balance.value, amountParsed.value), '0').toFixed())
|
||||
|
||||
const { liquidationPrice, status } = useLiquityPosition(changedCollateral, debt)
|
||||
|
||||
const balance = computed(() => getBalanceByKey(collateralToken.value.key))
|
||||
|
||||
const { toggle, isMaxAmount } = useMaxAmountActive(amount, balance)
|
||||
|
||||
const { validateAmount, validateLiquidation, validateIsLoggedIn, validateLiquityTroveExists } = useValidators()
|
||||
const errors = computed(() => {
|
||||
const hasAmountValue = !isZero(amount.value)
|
||||
|
||||
return {
|
||||
troveExists: { message: validateLiquityTroveExists(), show: true },
|
||||
amount: { message: validateAmount(amountParsed.value, balance.value), show: hasAmountValue },
|
||||
liquidation: { message: validateLiquidation(status.value, liquidation.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
|
||||
try {
|
||||
const inputAmountInWei = valInt(amountParsed.value, collateralToken.value.decimals)
|
||||
console.log(inputAmountInWei);
|
||||
const totalDepositAmountInWei = plus(inputAmountInWei, collateralInWei.value).toFixed()
|
||||
const { upperHint, lowerHint } = await getTrovePositionHints(totalDepositAmountInWei, debtInWei.value)
|
||||
|
||||
const spells = dsa.value.Spell()
|
||||
|
||||
const getId = 0
|
||||
const setId = 0
|
||||
|
||||
|
||||
spells.add({
|
||||
connector: 'LIQUITY-A',
|
||||
method: 'deposit',
|
||||
args: [inputAmountInWei, upperHint, lowerHint, getId, setId],
|
||||
})
|
||||
|
||||
const txHash = await dsa.value.cast({
|
||||
spells,
|
||||
from: account.value,
|
||||
})
|
||||
|
||||
showPendingTransaction(txHash)
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
showWarning(error.message)
|
||||
}
|
||||
|
||||
pending.value = false
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
return {
|
||||
collateralToken,
|
||||
balance,
|
||||
amount,
|
||||
status,
|
||||
liquidation,
|
||||
liquidationPrice,
|
||||
liquidationMaxPrice,
|
||||
formatUsd,
|
||||
formatUsdMax,
|
||||
formatDecimal,
|
||||
errors,
|
||||
errorMessages,
|
||||
isMaxAmount,
|
||||
isValid,
|
||||
cast,
|
||||
pending,
|
||||
toggle,
|
||||
changedCollateral,
|
||||
changedBalance,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
<template>
|
||||
<SidebarContextRootContainer>
|
||||
<template #title>Withdraw {{ collateralToken.symbol }}</template>
|
||||
|
||||
<SidebarSectionValueWithIcon label="Supplied" center>
|
||||
<template #icon
|
||||
><IconCurrency
|
||||
:currency="collateralToken.key"
|
||||
class="w-20 h-20"
|
||||
noHeight
|
||||
/></template>
|
||||
<template #value
|
||||
>{{ formatDecimal(changedCollateral) }}
|
||||
{{ collateralToken.symbol }}</template
|
||||
>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<SidebarSectionValueWithIcon label="Token Balance" center>
|
||||
<template #icon
|
||||
><IconCurrency
|
||||
:currency="collateralToken.key"
|
||||
class="w-20 h-20"
|
||||
noHeight
|
||||
/></template>
|
||||
<template #value
|
||||
>{{ formatDecimal(changedBalance) }}
|
||||
{{ collateralToken.symbol }}</template
|
||||
>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8">
|
||||
<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>
|
||||
|
||||
<SidebarContextHeading class="mt-5">
|
||||
Projected Debt Position
|
||||
</SidebarContextHeading>
|
||||
|
||||
<SidebarSectionStatus
|
||||
class="mt-8"
|
||||
:liquidation="liquidation"
|
||||
:status="status"
|
||||
/>
|
||||
|
||||
<SidebarSectionValueWithIcon class="mt-8" label="Liquidation Price (ETH)">
|
||||
<template #value>
|
||||
{{ formatUsdMax(liquidationPrice, liquidationMaxPrice) }}
|
||||
<span class="text-primary-gray"
|
||||
>/ {{ formatUsd(liquidationMaxPrice) }}</span
|
||||
>
|
||||
</template>
|
||||
</SidebarSectionValueWithIcon>
|
||||
|
||||
<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 { 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 '~/composables/useWeb3'
|
||||
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 { useLiquityPosition } from '~/composables/protocols/useLiquityPosition'
|
||||
|
||||
export default defineComponent({
|
||||
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
|
||||
setup() {
|
||||
const { close } = useSidebar()
|
||||
const { account } = useWeb3()
|
||||
const { dsa } = useDSA()
|
||||
const { valInt } = useToken()
|
||||
const { showPendingTransaction, showWarning } = useNotification()
|
||||
const { formatUsd, formatUsdMax, formatDecimal, formatNumber } = useFormatting()
|
||||
const { parseSafeFloat } = useParsing()
|
||||
const { isZero, minus, max, plus } = useBigNumber()
|
||||
|
||||
const amount = ref('')
|
||||
const amountParsed = computed(() => parseSafeFloat(amount.value))
|
||||
|
||||
const {
|
||||
collateralToken,
|
||||
debt,
|
||||
debtInWei,
|
||||
collateral,
|
||||
collateralInWei,
|
||||
liquidation,
|
||||
liquidationMaxPrice,
|
||||
getTrovePositionHints,
|
||||
} = useLiquityPosition()
|
||||
|
||||
const changedCollateral = computed(() => max(minus(collateral.value, amountParsed.value), '0').toFixed())
|
||||
const changedBalance = computed(() => max(plus(balance.value, amountParsed.value), '0').toFixed())
|
||||
const { liquidationPrice, status } = useLiquityPosition(changedCollateral, debt)
|
||||
|
||||
const { toggle, isMaxAmount } = useMaxAmountActive(amount, collateral)
|
||||
|
||||
const { validateAmount, validateLiquidation, validateIsLoggedIn, validateLiquityTroveExists } = useValidators()
|
||||
|
||||
const errors = computed(() => {
|
||||
const hasAmountValue = !isZero(amount.value)
|
||||
|
||||
return {
|
||||
troveExists: { message: validateLiquityTroveExists(), show: true },
|
||||
amount: { message: validateAmount(amountParsed.value, collateral.value), show: hasAmountValue },
|
||||
liquidation: {
|
||||
message: validateLiquidation(status.value, liquidation.value, isZero(debt.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 inputAmountInWei = valInt(amountParsed.value,collateralToken.value.decimals)
|
||||
const totalDepositAmountInWei = minus(collateralInWei.value, inputAmountInWei).toFixed()
|
||||
const { upperHint, lowerHint } = await getTrovePositionHints(totalDepositAmountInWei, debtInWei.value)
|
||||
|
||||
const getId = 0
|
||||
const setId = 0
|
||||
|
||||
const spells = dsa.value.Spell()
|
||||
|
||||
spells.add({
|
||||
connector: 'LIQUITY-A',
|
||||
method: 'withdraw',
|
||||
args: [inputAmountInWei, upperHint, lowerHint, getId, setId],
|
||||
})
|
||||
|
||||
try {
|
||||
const txHash = await dsa.value.cast({
|
||||
spells,
|
||||
from: account.value,
|
||||
})
|
||||
|
||||
showPendingTransaction(txHash)
|
||||
} catch (error) {
|
||||
showWarning(error.message)
|
||||
}
|
||||
|
||||
pending.value = false
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
return {
|
||||
collateralToken,
|
||||
collateral,
|
||||
debt,
|
||||
amount,
|
||||
status,
|
||||
liquidation,
|
||||
liquidationPrice,
|
||||
liquidationMaxPrice,
|
||||
formatUsd,
|
||||
formatUsdMax,
|
||||
formatDecimal,
|
||||
errors,
|
||||
errorMessages,
|
||||
isMaxAmount,
|
||||
isValid,
|
||||
cast,
|
||||
pending,
|
||||
toggle,
|
||||
changedBalance,
|
||||
changedCollateral,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
307
composables/protocols/useLiquityPosition.ts
Normal file
307
composables/protocols/useLiquityPosition.ts
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
import { computed, Ref, ref, watch } from "@nuxtjs/composition-api";
|
||||
import { useBalances } from "../useBalances";
|
||||
import { useBigNumber } from "../useBigNumber";
|
||||
import { useToken } from "../useToken";
|
||||
import { useWeb3 } from "~/composables/useWeb3";
|
||||
import { AbiItem } from "web3-utils";
|
||||
import BigNumber from "bignumber.js";
|
||||
BigNumber.config({ POW_PRECISION: 200 });
|
||||
import abis from "~/constant/abis";
|
||||
import addresses from "~/constant/addresses";
|
||||
import { useDSA } from "../useDSA";
|
||||
|
||||
const trove = ref<any>({
|
||||
collateral: "0",
|
||||
debt: "0",
|
||||
stabilityAmount: "0",
|
||||
stabilityEthGain: "0",
|
||||
stabilityLqtyGain: "0",
|
||||
stakeAmount: "0",
|
||||
stakeEthGain: "0",
|
||||
stakeLqtyGain: "0",
|
||||
price: "0",
|
||||
ratio: "0",
|
||||
tokenKey: "eth",
|
||||
token: "ETH",
|
||||
liquidation: "0"
|
||||
});
|
||||
|
||||
const troveTypes = ref([
|
||||
{
|
||||
totalCollateral: "0",
|
||||
price: "0",
|
||||
totalRatio: "0",
|
||||
tokenKey: "eth",
|
||||
token: "ETH",
|
||||
isRecoveryMode: false,
|
||||
borrowFee: "0",
|
||||
liquidation: "0",
|
||||
minDebt: "2000",
|
||||
liquidationReserve: "200"
|
||||
}
|
||||
]);
|
||||
|
||||
const troveOverallDetails = computed(() =>
|
||||
troveTypes.value.find(t => t.tokenKey === trove.value.tokenKey)
|
||||
);
|
||||
|
||||
export function useLiquityPosition(
|
||||
collateralAmountRef: Ref = null,
|
||||
debtAmountRef: Ref = null
|
||||
) {
|
||||
const { web3 } = useWeb3();
|
||||
const { activeAccount } = useDSA();
|
||||
|
||||
const { isZero, times, div, max, minus, plus } = useBigNumber();
|
||||
const { getTokenByKey, valInt } = useToken();
|
||||
const { prices } = useBalances();
|
||||
|
||||
const collateralToken = computed(() => getTokenByKey("eth"));
|
||||
const debtToken = computed(() => getTokenByKey("lusd"));
|
||||
const stakingToken = computed(() => getTokenByKey("lqty"));
|
||||
|
||||
const collateral = computed(() => trove.value.collateral);
|
||||
const collateralInWei = computed(() =>
|
||||
valInt(collateral.value, collateralToken.value?.decimals)
|
||||
);
|
||||
const priceInUsd = computed(() => trove.value.price);
|
||||
const ratio = computed(() => trove.value.ratio);
|
||||
const debt = computed(() => trove.value.debt);
|
||||
const debtInWei = computed(() => valInt(debt.value, debtToken.value?.decimals))
|
||||
const collateralUsd = computed(() =>
|
||||
times(collateral.value, priceInUsd.value).toFixed()
|
||||
);
|
||||
const stabilityAmount = computed(() => trove.value.stabilityAmount);
|
||||
const debtUsd = computed(() => times(debt.value, "1").toFixed());
|
||||
const stabilityAmountUsd = computed(() =>
|
||||
times(stabilityAmount.value, "1").toFixed()
|
||||
);
|
||||
|
||||
const stakingTokenPrice = computed(() =>
|
||||
stakingToken.value ? prices.mainnet[stakingToken.value.address] : "0"
|
||||
);
|
||||
const stakeAmount = computed(() => trove.value.stakeAmount);
|
||||
const stakeEthGain = computed(() => trove.value.stakeEthGain);
|
||||
const stakeLqtyGain = computed(() => trove.value.stakeLqtyGain);
|
||||
const stakingAmountUsd = computed(() =>
|
||||
times(stakeAmount.value, stakingTokenPrice.value).toFixed()
|
||||
);
|
||||
const netValue = computed(() =>
|
||||
plus(
|
||||
plus(minus(collateralUsd.value, debtUsd.value), stabilityAmountUsd.value),
|
||||
stakingAmountUsd.value
|
||||
).toFixed()
|
||||
);
|
||||
|
||||
const borrowFee = computed(() => troveOverallDetails.value.borrowFee);
|
||||
const maxFeePercentageInWei = computed(() =>
|
||||
times(times(borrowFee.value, "100"), "1e18").toFixed()
|
||||
);
|
||||
const liquidation = computed(() => troveOverallDetails.value.liquidation);
|
||||
|
||||
const status = computed(() => {
|
||||
if (!collateralAmountRef || !debtAmountRef) return ratio.value;
|
||||
return isZero(collateralAmountRef.value) && !isZero(debtAmountRef.value)
|
||||
? "1.1"
|
||||
: div(
|
||||
debtAmountRef.value,
|
||||
times(collateralAmountRef.value, priceInUsd.value)
|
||||
).toFixed();
|
||||
});
|
||||
|
||||
const liquidationPrice = computed(() => {
|
||||
if (!collateralAmountRef || !debtAmountRef) {
|
||||
return max(
|
||||
div(div(debt.value, collateral.value), liquidation.value),
|
||||
"0"
|
||||
).toFixed();
|
||||
}
|
||||
return isZero(collateralAmountRef.value) && !isZero(debtAmountRef.value)
|
||||
? times(priceInUsd.value, "1.1").toFixed()
|
||||
: max(
|
||||
div(
|
||||
div(debtAmountRef.value, collateralAmountRef.value),
|
||||
liquidation.value
|
||||
),
|
||||
"0"
|
||||
).toFixed();
|
||||
});
|
||||
|
||||
const troveOpened = computed(
|
||||
() => !isZero(collateral.value) && !isZero(debt.value)
|
||||
);
|
||||
|
||||
const minDebt = computed(() => troveOverallDetails.value.minDebt);
|
||||
const liquidationReserve = computed(
|
||||
() => troveOverallDetails.value.liquidationReserve
|
||||
);
|
||||
|
||||
const fetchPosition = async () => {
|
||||
if (!web3.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
troveTypes.value = await getTroveTypes(web3.value);
|
||||
|
||||
if (!activeAccount.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
trove.value = await getTrove(activeAccount.value.address, web3.value);
|
||||
};
|
||||
|
||||
async function getTrovePositionHints(collateralInWei, debtInWei) {
|
||||
try {
|
||||
const liquityInstance = new web3.value.eth.Contract(
|
||||
abis.resolver.liquity as AbiItem[],
|
||||
addresses.mainnet.resolver.liquity
|
||||
);
|
||||
|
||||
const {
|
||||
upperHint,
|
||||
lowerHint
|
||||
} = await liquityInstance.methods
|
||||
.getTrovePositionHints(
|
||||
collateralInWei.toString(),
|
||||
debtInWei.toString(),
|
||||
0,
|
||||
0
|
||||
)
|
||||
.call();
|
||||
|
||||
return {
|
||||
upperHint,
|
||||
lowerHint
|
||||
};
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
web3,
|
||||
async val => {
|
||||
if (val) {
|
||||
fetchPosition();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
activeAccount,
|
||||
async val => {
|
||||
if (val) {
|
||||
fetchPosition();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return {
|
||||
troveOpened,
|
||||
netValue,
|
||||
borrowFee,
|
||||
status,
|
||||
liquidation,
|
||||
liquidationPrice,
|
||||
liquidationMaxPrice: priceInUsd,
|
||||
collateralToken,
|
||||
debtToken,
|
||||
minDebt,
|
||||
liquidationReserve,
|
||||
maxFeePercentageInWei,
|
||||
getTrovePositionHints,
|
||||
collateral,
|
||||
collateralInWei,
|
||||
collateralUsd,
|
||||
priceInUsd,
|
||||
debt,
|
||||
debtInWei,
|
||||
};
|
||||
}
|
||||
|
||||
async function getTrove(user, web3) {
|
||||
const resolveABI = abis.resolver.liquity;
|
||||
const resolveAddr = addresses.mainnet.resolver.liquity;
|
||||
|
||||
const liquityInstance = new web3.eth.Contract(
|
||||
resolveABI as AbiItem[],
|
||||
resolveAddr
|
||||
);
|
||||
|
||||
try {
|
||||
const {
|
||||
trove,
|
||||
stake,
|
||||
stability
|
||||
} = await liquityInstance.methods.getPosition(user).call();
|
||||
const { collateral, debt, icr, price } = trove;
|
||||
const ratio =
|
||||
icr ===
|
||||
"115792089237316195423570985008687907853269984665640564039457584007913129639935"
|
||||
? "0"
|
||||
: new BigNumber(1e18).dividedBy(icr).toString();
|
||||
|
||||
return {
|
||||
collateral: new BigNumber(collateral).dividedBy(1e18).toString(),
|
||||
debt: new BigNumber(debt).dividedBy(1e18).toString(),
|
||||
stabilityAmount: new BigNumber(stability.deposit)
|
||||
.dividedBy(1e18)
|
||||
.toString(),
|
||||
stabilityEthGain: new BigNumber(stability.ethGain)
|
||||
.dividedBy(1e18)
|
||||
.toString(),
|
||||
stabilityLqtyGain: new BigNumber(stability.lqtyGain)
|
||||
.dividedBy(1e18)
|
||||
.toString(),
|
||||
stakeAmount: new BigNumber(stake.amount).dividedBy(1e18).toString(),
|
||||
stakeEthGain: new BigNumber(stake.ethGain).dividedBy(1e18).toString(),
|
||||
stakeLqtyGain: new BigNumber(stake.lusdGain).dividedBy(1e18).toString(),
|
||||
price: new BigNumber(price).dividedBy(1e18).toString(),
|
||||
ratio,
|
||||
tokenKey: "eth",
|
||||
token: "ETH",
|
||||
liquidation: ratio
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function getTroveTypes(web3) {
|
||||
try {
|
||||
const resolveABI = abis.resolver.liquity;
|
||||
const resolveAddr = addresses.mainnet.resolver.liquity;
|
||||
|
||||
const liquityInstance = new web3.eth.Contract(
|
||||
resolveABI as AbiItem[],
|
||||
resolveAddr
|
||||
);
|
||||
const {
|
||||
borrowFee,
|
||||
ethTvl,
|
||||
isInRecoveryMode: isRecoveryMode,
|
||||
tcr,
|
||||
price
|
||||
} = await liquityInstance.methods.getSystemState().call();
|
||||
|
||||
return [
|
||||
{
|
||||
totalCollateral: new BigNumber(ethTvl).dividedBy(1e18).toString(),
|
||||
price: new BigNumber(price).dividedBy(1e18).toString(),
|
||||
totalRatio: new BigNumber(1e18).dividedBy(tcr).toString(),
|
||||
tokenKey: "eth",
|
||||
token: "ETH",
|
||||
isRecoveryMode,
|
||||
borrowFee: new BigNumber(borrowFee).dividedBy(1e18).toString(),
|
||||
liquidation: new BigNumber(100).dividedBy(110).toString(),
|
||||
minDebt: new BigNumber(2000).toString(),
|
||||
liquidationReserve: "200"
|
||||
}
|
||||
];
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,13 @@ import SidebarMakerdaoWithdraw from '~/components/sidebar/context/makerdao/Sideb
|
|||
import SidebarMakerdaoBorrow from '~/components/sidebar/context/makerdao/SidebarMakerdaoBorrow.vue'
|
||||
import SidebarMakerdaoPayback from '~/components/sidebar/context/makerdao/SidebarMakerdaoPayback.vue'
|
||||
|
||||
import SidebarLiquityTroveOpenNew from '~/components/sidebar/context/liquity/SidebarLiquityTroveOpenNew.vue'
|
||||
import SidebarLiquityTroveSupply from '~/components/sidebar/context/liquity/SidebarLiquityTroveSupply.vue'
|
||||
import SidebarLiquityTroveWithdraw from '~/components/sidebar/context/liquity/SidebarLiquityTroveWithdraw.vue'
|
||||
import SidebarLiquityTroveBorrow from '~/components/sidebar/context/liquity/SidebarLiquityTroveBorrow.vue'
|
||||
import SidebarLiquityTrovePayback from '~/components/sidebar/context/liquity/SidebarLiquityTrovePayback.vue'
|
||||
|
||||
|
||||
const sidebars = {
|
||||
"#overview" : {component: SidebarOverview, back : false, close : true },
|
||||
"#deposit-overview": {component: SidebarDepositOverview, back: { hash: 'overview' } },
|
||||
|
|
@ -59,6 +66,12 @@ const sidebars = {
|
|||
"/mainnet/maker#withdraw": { component: SidebarMakerdaoWithdraw },
|
||||
"/mainnet/maker#borrow": { component: SidebarMakerdaoBorrow },
|
||||
"/mainnet/maker#payback": { component: SidebarMakerdaoPayback },
|
||||
|
||||
'/mainnet/liquity#trove-new': { component: SidebarLiquityTroveOpenNew },
|
||||
'/mainnet/liquity#trove-supply': { component: SidebarLiquityTroveSupply },
|
||||
'/mainnet/liquity#trove-withdraw': { component: SidebarLiquityTroveWithdraw },
|
||||
'/mainnet/liquity#trove-borrow': { component: SidebarLiquityTroveBorrow },
|
||||
'/mainnet/liquity#trove-payback': { component: SidebarLiquityTrovePayback },
|
||||
};
|
||||
|
||||
const sidebar = ref(null);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
import { useBigNumber } from "./useBigNumber";
|
||||
import { useFormatting } from "./useFormatting";
|
||||
import { useMakerdaoPosition } from "~/composables/protocols/useMakerdaoPosition";
|
||||
import { useLiquityPosition } from "./protocols/useLiquityPosition";
|
||||
|
||||
export function useValidators() {
|
||||
const { formatNumber } = useFormatting();
|
||||
const { isZero, minus, eq, gt, lt, gte, plus } = useBigNumber();
|
||||
const { minDebt: makerMinDebt, vaultTypes } = useMakerdaoPosition();
|
||||
|
||||
const {
|
||||
minDebt: liquityMinDebt,
|
||||
liquidationReserve: liquityLiquidationReserve,
|
||||
troveOpened: liquityTroveOpened
|
||||
} = useLiquityPosition();
|
||||
|
||||
function validateAmount(amountParsed, balance = null, options = null) {
|
||||
const mergedOptions = Object.assign(
|
||||
{ msg: "Your amount exceeds your maximum limit." },
|
||||
|
|
@ -90,12 +96,39 @@ export function useValidators() {
|
|||
return null;
|
||||
}
|
||||
|
||||
function validateLiquityDebt(
|
||||
debtParsed,
|
||||
minDebt = liquityMinDebt.value,
|
||||
liquidationReserve = liquityLiquidationReserve.value
|
||||
) {
|
||||
const totalDebt = plus(debtParsed, liquidationReserve);
|
||||
|
||||
if (isZero(totalDebt))
|
||||
return `Minimum total debt requirement is ${minDebt} LUSD`;
|
||||
|
||||
if (lt(totalDebt, minDebt) && gt(totalDebt, "0")) {
|
||||
return `Minimum total debt requirement is ${minDebt} LUSD`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function validateLiquityTroveExists() {
|
||||
if (!liquityTroveOpened.value) {
|
||||
return "You should open new trove first";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
validateAmount,
|
||||
validateLiquidation,
|
||||
validateIsLoggedIn,
|
||||
validateLiquidity,
|
||||
validateMakerDebt,
|
||||
validateMakerDebtCeiling
|
||||
validateMakerDebtCeiling,
|
||||
validateLiquityDebt,
|
||||
validateLiquityTroveExists
|
||||
};
|
||||
}
|
||||
|
|
|
|||
361
constant/abi/read/liquity.json
Normal file
361
constant/abi/read/liquity.json
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
[
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "fetchETHPrice",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getPosition",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "collateral",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "debt",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "icr",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "price",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Helpers.Trove",
|
||||
"name": "trove",
|
||||
"type": "tuple"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "deposit",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "ethGain",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "lqtyGain",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Helpers.StabilityDeposit",
|
||||
"name": "stability",
|
||||
"type": "tuple"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "ethGain",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "lusdGain",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Helpers.Stake",
|
||||
"name": "stake",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Helpers.Position",
|
||||
"name": "",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "searchIterations",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "randomSeed",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getRedemptionPositionHints",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "partialHintNicr",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "firstHint",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "upperHint",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "lowerHint",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "oracleEthPrice",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getStabilityDeposit",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "deposit",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "ethGain",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "lqtyGain",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Helpers.StabilityDeposit",
|
||||
"name": "",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getStake",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "ethGain",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "lusdGain",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Helpers.Stake",
|
||||
"name": "",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getSystemState",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "borrowFee",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "ethTvl",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "tcr",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "isInRecoveryMode",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "price",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Helpers.System",
|
||||
"name": "",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getTrove",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "collateral",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "debt",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "icr",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "price",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Helpers.Trove",
|
||||
"name": "",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "collateral",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "debt",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "searchIterations",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "randomSeed",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getTrovePositionHints",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "upperHint",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "lowerHint",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "name",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
|
|
@ -6,6 +6,7 @@ import compoundABI from "./abi/read/compound.json";
|
|||
import makerABI from "./abi/read/maker.json";
|
||||
import makerProxyRegistryABI from "./abi/makerProxyRegistry.json";
|
||||
import unipoolABI from "./abi/read/unipool.json";
|
||||
import liquityABI from "./abi/read/liquity.json";
|
||||
|
||||
const abis = {
|
||||
makerProxyRegistry: makerProxyRegistryABI,
|
||||
|
|
@ -17,7 +18,8 @@ const abis = {
|
|||
compound: compoundABI,
|
||||
maker: makerABI,
|
||||
unipool: unipoolABI,
|
||||
},
|
||||
liquity: liquityABI,
|
||||
}
|
||||
};
|
||||
|
||||
export default abis;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const addresses = {
|
|||
compound: "0xcCAa4b1b3931749b8b6EF19C6b0B2c496703321b",
|
||||
maker: "0x84addce4fac0b6ee4b0cd132120d6d4b700e35c0",
|
||||
unipool: "0x22bddA39D14eD0aafeee36B6e784602fdDE64723",
|
||||
liquity: '0xDAf2A39503463B0F41f899EDD82213b3c96b6Cf8',
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export default {
|
|||
{ key: 'mkr', type: 'token', symbol: 'MKR', name: 'MakerDAO', address: '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', decimals: 18, isStableCoin: false},
|
||||
{ key: 'comp', type: 'token', symbol: 'COMP', name: 'Compound', address: '0xc00e94Cb662C3520282E6f5717214004A7f26888', decimals: 18, isStableCoin: false},
|
||||
{ key: 'rai', type: 'token', symbol: 'RAI', name: 'Rai Reflex Index', address: '0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919', decimals: 18, isStableCoin: false},
|
||||
{ key: 'lusd', type: 'token', symbol: 'LUSD', name: 'Liquity USD', address: '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', decimals: 18, isStableCoin: true},
|
||||
{ key: 'zrx', type: 'token', symbol: 'ZRX', name: '0x Protocol', address: '0xE41d2489571d322189246DaFA5ebDe1F4699F498', decimals: 18, isStableCoin: false},
|
||||
{ key: 'rep', type: 'token', symbol: 'REP', name: 'Augur', address: '0x1985365e9f78359a9B6AD760e32412f4a445E862', decimals: 18, isStableCoin: false},
|
||||
{ key: 'tusd', type: 'token', symbol: 'TUSD', name: 'TrueUSD', address: '0x0000000000085d4780B73119b644AE5ecd22b376', decimals: 18, isStableCoin: true},
|
||||
|
|
@ -44,7 +45,8 @@ export default {
|
|||
{ key: 'eth2xfli', type: 'token', symbol: 'ETH2x-FLI', name: 'ETH 2x Flexible Leverage Index', address: '0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD', decimals: 18, isStableCoin: false, },
|
||||
{ key: 'btc2xfli', type: 'token', symbol: 'BTC2x-FLI', name: 'BTC 2x Flexible Leverage Index', address: '0x0B498ff89709d3838a063f1dFA463091F9801c2b', decimals: 18, isStableCoin: false, },
|
||||
{ key: 'stkaave', type: 'token', symbol: 'stkAAVE', name: 'Staked Aave', address: '0x4da27a545c0c5B758a6BA100e3a049001de870f5', decimals: 18, isStableCoin: false, },
|
||||
{ key: 'matic', type: 'token', symbol: 'MATIC', name: 'Matic Token', address: '0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', decimals: 18, isStableCoin: false, }
|
||||
{ key: 'matic', type: 'token', symbol: 'MATIC', name: 'Matic Token', address: '0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', decimals: 18, isStableCoin: false, },
|
||||
{ key: 'lqty', type: 'token', symbol: 'LQTY', name: 'LQTY', address: '0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D', decimals: 18, isStableCoin: false},
|
||||
]),
|
||||
|
||||
polygon: createTokenUtils([
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"bignumber.js": "^9.0.1",
|
||||
"core-js": "^3.15.1",
|
||||
"css-color-function": "^1.3.3",
|
||||
"dsa-connect": "^0.4.2",
|
||||
"dsa-connect": "^0.4.3",
|
||||
"nuxt": "^2.15.7",
|
||||
"qrcode": "^1.4.4",
|
||||
"v-click-outside": "^3.1.2",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<div
|
||||
class="w-12 h-12 rounded-full flex items-center justify-center bg-[#1874FF]"
|
||||
>
|
||||
<OneInchIcon Icon class="w-10 h-10 text-white" />
|
||||
<OneInchIcon Icon class="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="ml-4 text-primary-black text-2xl font-semibold">1Inch</h1>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import AaveIcon from "~/assets/icons/aave.svg?inline";
|
|||
import CompoundIcon from "~/assets/icons/compound.svg?inline";
|
||||
import MakerIcon from "~/assets/icons/makerdao.svg?inline";
|
||||
import OneInchIcon from "~/assets/icons/1inch.svg?inline";
|
||||
import LiquityIcon from "~/assets/icons/liquity.svg?inline";
|
||||
|
||||
const appsPerNetwork = {
|
||||
mainnet: [
|
||||
|
|
@ -66,6 +67,13 @@ const appsPerNetwork = {
|
|||
name: "1inch",
|
||||
url: "/1inch",
|
||||
description: "DEX Aggregator"
|
||||
},
|
||||
{
|
||||
id: "liquity",
|
||||
icon: LiquityIcon,
|
||||
name: "Liquity",
|
||||
url: "/mainnet/liquity",
|
||||
description: "Collateralized Debt"
|
||||
}
|
||||
],
|
||||
polygon: [
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<div
|
||||
class="w-12 h-12 rounded-full flex items-center justify-center bg-[#1874FF]"
|
||||
>
|
||||
<AaveIcon class="w-10 h-10 text-white" />
|
||||
<AaveIcon class="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="ml-4 text-primary-black text-2xl font-semibold">Aave v2</h1>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<div
|
||||
class="w-12 h-12 rounded-full flex items-center justify-center bg-[#1874FF]"
|
||||
>
|
||||
<CompoundIcon class="w-10 h-10 text-white" />
|
||||
<CompoundIcon class="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="ml-4 text-primary-black text-2xl font-semibold">Compound</h1>
|
||||
|
|
|
|||
229
pages/mainnet/liquity.vue
Normal file
229
pages/mainnet/liquity.vue
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
<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">
|
||||
<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]"
|
||||
>
|
||||
<LiquityIcon class="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="ml-4 text-primary-black text-2xl font-semibold">Liquity</h1>
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
<h2 class="text-primary-gray text-lg font-semibold">Overview</h2>
|
||||
|
||||
<div
|
||||
class="px-1 mt-6 grid w-full grid-cols-1 gap-4 sm:grid-cols-3 xl:gap-[18px]"
|
||||
>
|
||||
<div class="shadow rounded-lg py-8 px-6 flex">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-2xl text-primary-black font-medium">
|
||||
{{ formatUsd(netValue) }}
|
||||
</h3>
|
||||
<p class="mt-4 text-primary-gray font-medium">Net Value</p>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<SVGBalance />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shadow rounded-lg py-8 px-6 flex">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-2xl text-primary-black font-medium">
|
||||
{{ formatPercent(borrowFee) }}
|
||||
</h3>
|
||||
<p class="mt-4 text-primary-gray font-medium">Borrow Fee</p>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<SVGPercent class="h-12" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shadow rounded-lg py-8 px-6 flex">
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-between items-center">
|
||||
<h3 class="text-2xl text-primary-black font-medium">
|
||||
{{ formatPercent(status) }}
|
||||
</h3>
|
||||
<Badge class="w-18 xxl:w-23" :color="color">{{ text }}</Badge>
|
||||
</div>
|
||||
<div
|
||||
class="mt-4 flex justify-between items-center text-primary-gray font-medium"
|
||||
>
|
||||
<div class="flex items-center whitespace-no-wrap">
|
||||
<div>D/C (%)</div>
|
||||
|
||||
<div class="ml-2"><Info text="Debt/Collateral ratio" /></div>
|
||||
</div>
|
||||
<span>Max - {{ formatPercent(liquidation) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shadow rounded-lg py-8 px-6 flex">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-2xl text-primary-black font-medium">
|
||||
{{ formatUsdMax(liquidationPrice, liquidationMaxPrice) }} /
|
||||
{{ formatUsd(liquidationMaxPrice) }}
|
||||
</h3>
|
||||
<p class="mt-4 text-primary-gray font-medium">Liquidation (ETH)</p>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<IconBackground
|
||||
name="receipt-tax"
|
||||
class="bg-light-brown-pure text-light-brown-pure"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<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"
|
||||
>
|
||||
<CardLiquityTrove
|
||||
v-if="troveOpened"
|
||||
:collateral="collateral"
|
||||
:amount-usd="collateralUsd"
|
||||
:price-in-usd="priceInUsd"
|
||||
position-type="supply"
|
||||
:token="collateralToken"
|
||||
/>
|
||||
|
||||
<CardLiquityTrove
|
||||
v-if="troveOpened"
|
||||
:debt="debt"
|
||||
:collateral="collateral"
|
||||
:amount-usd="debt"
|
||||
price-in-usd="1"
|
||||
position-type="borrow"
|
||||
:token="debtToken"
|
||||
/>
|
||||
|
||||
<button-dashed
|
||||
v-if="!troveOpened"
|
||||
color="ocean-blue"
|
||||
class="col-span-full"
|
||||
height="80px"
|
||||
full-width
|
||||
@click="openNewTrove"
|
||||
>
|
||||
<SVGAdd class="w-3 mr-2" />
|
||||
Open Trove
|
||||
</button-dashed>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, useRouter } from "@nuxtjs/composition-api";
|
||||
import BackIcon from "~/assets/icons/back.svg?inline";
|
||||
import SVGBalance from "@/assets/img/icons/balance.svg?inline";
|
||||
import SVGPercent from "@/assets/img/icons/percent.svg?inline";
|
||||
import SVGAdd from "~/assets/img/icons/add.svg?inline";
|
||||
import LiquityIcon from "~/assets/icons/liquity.svg?inline";
|
||||
import ButtonDashed from "~/components/common/input/ButtonDashed.vue";
|
||||
import { useLiquityPosition } from "~/composables/protocols/useLiquityPosition";
|
||||
import { useFormatting } from "~/composables/useFormatting";
|
||||
import { useStatus } from "~/composables/useStatus";
|
||||
import { useBigNumber } from "~/composables/useBigNumber";
|
||||
import CardLiquityTrove from "~/components/protocols/liquity/CardLiquityTrove.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
BackIcon,
|
||||
LiquityIcon,
|
||||
ButtonDashed,
|
||||
SVGAdd,
|
||||
SVGBalance,
|
||||
SVGPercent,
|
||||
CardLiquityTrove
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
|
||||
const { div, isZero, gt, lt } = useBigNumber();
|
||||
|
||||
const {
|
||||
formatUsd,
|
||||
formatUsdMax,
|
||||
formatPercent,
|
||||
formatDecimal
|
||||
} = useFormatting();
|
||||
|
||||
const {
|
||||
troveOpened,
|
||||
netValue,
|
||||
borrowFee,
|
||||
status,
|
||||
liquidation,
|
||||
liquidationPrice,
|
||||
liquidationMaxPrice,
|
||||
collateral,
|
||||
collateralUsd,
|
||||
priceInUsd,
|
||||
debt,
|
||||
debtToken,
|
||||
collateralToken
|
||||
} = useLiquityPosition();
|
||||
|
||||
const statusLiquidationRatio = computed(() =>
|
||||
div(status.value, liquidation.value).toFixed()
|
||||
);
|
||||
|
||||
const { color, text } = useStatus(statusLiquidationRatio);
|
||||
|
||||
function openNewTrove() {
|
||||
router.push({ hash: "trove-new" });
|
||||
}
|
||||
|
||||
return {
|
||||
color,
|
||||
text,
|
||||
|
||||
formatUsd,
|
||||
formatUsdMax,
|
||||
formatPercent,
|
||||
formatDecimal,
|
||||
|
||||
troveOpened,
|
||||
netValue,
|
||||
borrowFee,
|
||||
status,
|
||||
liquidation,
|
||||
liquidationPrice,
|
||||
liquidationMaxPrice,
|
||||
collateral,
|
||||
collateralUsd,
|
||||
priceInUsd,
|
||||
debt,
|
||||
debtToken,
|
||||
collateralToken,
|
||||
|
||||
openNewTrove
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<div
|
||||
class="w-12 h-12 rounded-full flex items-center justify-center bg-[#1874FF]"
|
||||
>
|
||||
<MakerDAOIcon Icon class="w-10 h-10 text-white" />
|
||||
<MakerDAOIcon Icon class="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="ml-4 text-primary-black text-2xl font-semibold">MakerDAO</h1>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<div
|
||||
class="w-12 h-12 rounded-full flex items-center justify-center bg-[#1874FF]"
|
||||
>
|
||||
<AaveIcon class="w-10 h-10 text-white" />
|
||||
<AaveIcon class="w-8 h-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="ml-4 text-primary-black text-2xl font-semibold">Aave v2</h1>
|
||||
|
|
|
|||
|
|
@ -4675,10 +4675,10 @@ drbg.js@^1.0.1:
|
|||
create-hash "^1.1.2"
|
||||
create-hmac "^1.1.4"
|
||||
|
||||
dsa-connect@^0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/dsa-connect/-/dsa-connect-0.4.2.tgz#1fb200eb3a93d260acada2cdbe8d5ebb71e3ba95"
|
||||
integrity sha512-NW9kblkfEgoYx7uyz0TiWauIya09XMnyDQ/kRCleSRo9wAmK3gGLLuPXMAtkk8P2jvgMxIce9zeJvFvYcGdX8Q==
|
||||
dsa-connect@^0.4.3:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/dsa-connect/-/dsa-connect-0.4.3.tgz#382ad7c1b1fa54f963c84adc353dea4bde2f6e80"
|
||||
integrity sha512-kbG46cvAR2muy2P5jOTVsSZmyDewTAA0lKAeddsrVtpbmys1ujSuDjY/su4At6fAg/iF9/k+ltJAOEvoqt6g9A==
|
||||
|
||||
duplexer3@^0.1.4:
|
||||
version "0.1.4"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user