This commit is contained in:
Georges KABBOUCHI 2021-08-08 02:22:42 +03:00
parent b93cfc2659
commit 7b558c52f1
18 changed files with 932 additions and 84 deletions

View File

@ -0,0 +1,5 @@
<svg viewBox="0 0 27 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M1.38136 25.7159C0.959472 25.4686 0.610963 25.122 0.369124 24.7093C0.127286 24.2965 0.000196677 23.8313 0 23.3582V17.9946C0 17.792 0.0593228 17.5895 0.161018 17.4193C0.491526 16.8684 1.23729 16.682 1.81356 17.0061L14.4746 24.0631C15.2119 24.4763 15.6695 25.2298 15.6695 26.0562V31.6143C15.6695 31.8655 15.5932 32.1166 15.4576 32.3354C15.0424 32.9916 14.1525 33.1942 13.4661 32.7972L1.38136 25.7159ZM20.2627 15.5315C21 15.9447 21.4576 16.6982 21.4576 17.5247V28.8028C21.4576 29.135 21.2712 29.4429 20.9661 29.605L18.1949 31.0957C18.161 31.1119 18.1186 31.1282 18.0847 31.1363V24.8733C18.0847 24.0631 17.6441 23.3096 16.9153 22.8964L5.79661 16.5362V9.47113C5.79661 9.26857 5.85593 9.06602 5.95763 8.89587C6.28814 8.34493 7.0339 8.15858 7.61017 8.48267L20.2627 15.5315ZM25.7966 7.20253C26.5424 7.61574 27 8.37734 27 9.19565V25.6673C27 26.0076 26.8051 26.3155 26.4915 26.4775L23.8644 27.8306V16.3661C23.8644 15.5558 23.4237 14.8104 22.6949 14.3891L11.3305 7.8669V1.15833C11.3305 0.955775 11.3898 0.753221 11.4915 0.574974C11.822 0.0240284 12.5678 -0.162322 13.1441 0.153662L25.7966 7.20253Z"
fill="#00B04C" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,8 @@
<svg viewBox="0 0 32 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M14.7847 20.0949V11.1577C14.7847 11.1577 14.7847 9.97337 13.7835 9.3266L1.81138 0.809359C1.81138 0.809359 0.413105 0.196186 0.214534 1.62412V19.8933C0.214534 19.8933 0.18144 20.8845 1.01709 21.1113C1.01709 21.1113 2.41536 21.5817 2.41536 19.6917V5.07638C2.41536 5.07638 2.23334 3.95923 3.21792 4.6732C4.19423 5.38716 11.8061 10.7629 11.8061 10.7629C11.8061 10.7629 12.4018 11.0065 12.4018 11.7793V20.1033C12.4018 20.1033 12.3191 21.3297 13.6015 21.3213C14.8674 21.3045 14.7847 20.0949 14.7847 20.0949Z"
fill="#1AAB9A" />
<path
d="M17.2153 20.0949V11.1577C17.2153 11.1577 17.2153 9.97337 18.2165 9.3266L30.1886 0.809359C30.1886 0.809359 31.5869 0.196186 31.7855 1.62412V19.8933C31.7855 19.8933 31.8186 20.8845 30.9829 21.1113C30.9829 21.1113 29.5846 21.5817 29.5846 19.6917V5.07638C29.5846 5.07638 29.7667 3.95923 28.7821 4.6732C27.8058 5.38716 20.1939 10.7629 20.1939 10.7629C20.1939 10.7629 19.5982 11.0065 19.5982 11.7793V20.1033C19.5982 20.1033 19.6809 21.3297 18.3985 21.3213C17.1326 21.3045 17.2153 20.0949 17.2153 20.0949Z"
fill="#1AAB9A" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -19,12 +19,12 @@
<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
Amount to borrow
</h3>
<input-numeric
v-model="amount"
placeholder="Amount to supply"
placeholder="Amount to borrow"
:error="errors.amount.message"
>
<template v-if="!isMaxAmount" #suffix>

View File

@ -11,12 +11,12 @@
<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
Amount to withdraw
</h3>
<input-numeric
v-model="amount"
placeholder="Amount to supply"
placeholder="Amount to withdraw"
:error="errors.amount.message"
>
<template v-if="!isMaxAmount" #suffix>

View File

@ -0,0 +1,210 @@
<template>
<SidebarContextRootContainer>
<template #title>Borrow {{ symbol }}</template>
<SidebarSectionValueWithIcon class="mt-6" label="Borrowed" center>
<template #icon
><IconCurrency :currency="rootTokenKey" class="w-20 h-20" noHeight
/></template>
<template #value>{{ formatNumber(balance) }} {{ symbol }}</template>
</SidebarSectionValueWithIcon>
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8">
<h3 class="text-primary-gray text-xs font-semibold mb-2.5">
Amount to borrow
</h3>
<input-numeric
v-model="amount"
placeholder="Amount to borrow"
: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"
>
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 { 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 { useCompoundPosition } from '~/composables/useCompoundPosition'
import ctokens from '~/constant/ctokens'
import tokenIdMapping from '~/constant/tokenIdMapping'
export default defineComponent({
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
props: {
tokenId: { type: String, required: true },
},
setup(props) {
const { close } = useSidebar()
const { networkName, account } = useWeb3()
const { dsa } = useDSA()
const { getTokenByKey, valInt } = useToken()
const { formatNumber, formatUsdMax, formatUsd } = useFormatting()
const { isZero, gt, div, plus } = useBigNumber()
const { parseSafeFloat } = useParsing()
const { showPendingTransaction } = useNotification()
const tokenId = computed(() => props.tokenId)
const tokenKey = computed(() => tokenIdMapping.idToToken[tokenId.value])
const rootTokenKey = computed(() => ctokens[networkName.value].rootTokens.includes(tokenKey.value) ? tokenKey.value : 'eth')
const { stats, status: initialStatus, displayPositions, liquidation, liquidationPrice, liquidationMaxPrice } = useCompoundPosition({
overridePosition: (position) => {
if (tokenId.value !== position.cTokenId) return position
return {
...position,
borrow: plus(position.borrow, amountParsed.value).toFixed(),
}
},
})
const status = computed(() => {
if (!amountParsed.value) return initialStatus.value
return div(stats.value.totalBorrowInEth, stats.value.totalSupplyInEth).toFixed()
})
const amount = ref('')
const amountParsed = computed(() => parseSafeFloat(amount.value))
const currentPosition = computed(() =>
displayPositions.value.find((position) => position.cTokenId === tokenId.value)
)
const token = computed(() => getTokenByKey(rootTokenKey.value))
const symbol = computed(() => token.value?.symbol)
const decimals = computed(() => token.value?.decimals)
const balance = computed(() => {
return currentPosition.value?.borrow || '0'
})
const address = computed(() => token.value?.address)
const { toggle, isMaxAmount } = useMaxAmountActive(amount, balance)
const { validateAmount, validateLiquidation, validateLiquidity, validateIsLoggedIn } = useValidators()
const errors = computed(() => {
const hasAmountValue = !isZero(amount.value)
return {
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
const amount = valInt(amountParsed.value, decimals.value)
const spells = dsa.value.Spell()
spells.add({
connector: 'compound',
method: 'borrow',
args: [tokenId.value, amount, 0, 0],
})
const txHash = await dsa.value.cast({
spells,
from: account.value,
})
showPendingTransaction(txHash)
pending.value = false
close()
}
return {
stats,
pending,
cast,
errors,
amount,
status,
liquidation,
rootTokenKey,
token,
symbol,
balance,
formatNumber,
formatUsdMax,
formatUsd,
toggle,
isMaxAmount,
liquidationPrice,
liquidationMaxPrice,
errorMessages,
isValid,
}
},
})
</script>

View File

@ -0,0 +1,225 @@
<template>
<SidebarContextRootContainer>
<template #title>Payback {{ symbol }}</template>
<div class="mt-6 flex justify-around items-center w-full">
<SidebarSectionValueWithIcon class="" label="Borrowed" center>
<template #icon
><IconCurrency :currency="rootTokenKey" class="w-20 h-20" noHeight
/></template>
<template #value>{{ formatNumber(balance) }} {{ symbol }}</template>
</SidebarSectionValueWithIcon>
<SidebarSectionValueWithIcon class="" label="Token Balance" center>
<template #icon
><IconCurrency :currency="rootTokenKey" class="w-20 h-20" noHeight
/></template>
<template #value
>{{ formatNumber(tokenMaxBalance) }} {{ 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 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"
>
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 { useCompoundPosition } from '~/composables/useCompoundPosition'
import ctokens from '~/constant/ctokens'
import tokenIdMapping from '~/constant/tokenIdMapping'
export default defineComponent({
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
props: {
tokenId: { type: String, required: true },
},
setup(props) {
const { close } = useSidebar()
const { networkName, account } = useWeb3()
const { dsa } = useDSA()
const { getTokenByKey, valInt } = useToken()
const { getBalanceByKey, getBalanceRawByKey, fetchBalances } = useBalances()
const { formatNumber, formatUsdMax, formatUsd } = useFormatting()
const { isZero, gt, plus, max, minus } = useBigNumber()
const { parseSafeFloat } = useParsing()
const { showPendingTransaction } = useNotification()
const tokenId = computed(() => props.tokenId)
const tokenKey = computed(() => tokenIdMapping.idToToken[tokenId.value])
const rootTokenKey = computed(() => ctokens[networkName.value].rootTokens.includes(tokenKey.value) ? tokenKey.value : 'eth')
const { status, displayPositions, liquidation, liquidationPrice, liquidationMaxPrice } = useCompoundPosition({
overridePosition: (position) => {
if (tokenId.value !== position.cTokenId) return position
return {
...position,
borrow: max(minus(position.borrow, amountParsed.value), '0').toFixed(),
}
},
})
const amount = ref('')
const amountParsed = computed(() => parseSafeFloat(amount.value))
const currentPosition = computed(() =>
displayPositions.value.find((position) => position.cTokenId === tokenId.value)
)
const token = computed(() => getTokenByKey(rootTokenKey.value))
const symbol = computed(() => token.value?.symbol)
const decimals = computed(() => token.value?.decimals)
const balance = computed(() => {
return currentPosition.value?.borrow || '0'
})
const tokenMaxBalance = computed(() => getBalanceByKey(rootTokenKey.value))
const tokenMaxBalanceRaw = computed(() => getBalanceRawByKey(rootTokenKey.value))
const address = computed(() => token.value?.address)
const { toggle, isMaxAmount } = useMaxAmountActive(amount, balance)
const { validateAmount, validateLiquidation, validateLiquidity, validateIsLoggedIn } = useValidators()
const errors = computed(() => {
const hasAmountValue = !isZero(amount.value)
return {
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
const amount = isMaxAmount.value
? gte(tokenMaxBalance.value, balance.value)
? $dsa().maxValue
: tokenMaxBalanceRaw.value
: valInt(amountParsed.value, decimals.value)
const spells = dsa.value.Spell()
const rateMode = rateType.value?.rateMode
spells.add({
connector: 'compound',
method: 'payback',
args: [tokenId.value, amount, 0, 0],
})
const txHash = await dsa.value.cast({
spells,
from: account.value,
})
showPendingTransaction(txHash)
pending.value = false
close()
}
return {
pending,
cast,
errors,
amount,
status,
rootTokenKey,
token,
symbol,
balance,
formatNumber,
formatUsdMax,
formatUsd,
toggle,
isMaxAmount,
liquidationPrice,
liquidationMaxPrice,
errorMessages,
isValid,
tokenMaxBalance,
}
},
})
</script>

View File

@ -0,0 +1,203 @@
<template>
<SidebarContextRootContainer>
<template #title>Supply {{ symbol }}</template>
<SidebarSectionValueWithIcon label="Token Balance" center>
<template #icon
><IconCurrency :currency="rootTokenKey" class="w-20 h-20" noHeight
/></template>
<template #value>{{ formatNumber(balance) }} {{ symbol }}</template>
</SidebarSectionValueWithIcon>
<div class="bg-[#C5CCE1] bg-opacity-[0.15] mt-10 p-8">
<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="maxLiquidation"
: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 tokenIdMapping from '~/constant/tokenIdMapping'
import ctokens from '~/constant/ctokens'
import { useCompoundPosition } from '~/composables/useCompoundPosition'
export default defineComponent({
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
props: {
tokenId: { type: String, required: true },
},
setup(props) {
const { close } = useSidebar()
const { networkName, account } = useWeb3()
const { dsa } = useDSA()
const { getTokenByKey, valInt } = useToken()
const { getBalanceByKey, fetchBalances } = useBalances()
const { formatNumber, formatUsdMax, formatUsd } = useFormatting()
const { isZero, gt, plus } = useBigNumber()
const { parseSafeFloat } = useParsing()
const { showPendingTransaction } = useNotification()
const tokenId = computed(() => props.tokenId)
const tokenKey = computed(() => tokenIdMapping.idToToken[tokenId.value])
const rootTokenKey = computed(() => ctokens[networkName.value].rootTokens.includes(tokenKey.value) ? tokenKey.value : 'eth')
const { status, displayPositions, maxLiquidation, liquidationPrice, liquidationMaxPrice } = useCompoundPosition({
overridePosition: (position) => {
if (tokenId.value !== position.cTokenId) return position
return {
...position,
supply: plus(position.supply, amountParsed.value).toFixed(),
}
},
})
const amount = ref('')
const amountParsed = computed(() => parseSafeFloat(amount.value))
const token = computed(() => getTokenByKey(rootTokenKey.value))
const symbol = computed(() => token.value?.symbol)
const decimals = computed(() => token.value?.decimals)
const balance = computed(() => getBalanceByKey(rootTokenKey.value))
const address = computed(() => token.value?.address)
const factor = computed(
() => displayPositions.value?.find((position) => position.cTokenId === tokenId.value)?.factor
)
const { toggle, isMaxAmount } = useMaxAmountActive(amount, balance)
const { validateAmount, validateLiquidation, validateIsLoggedIn } = useValidators()
const errors = computed(() => {
const hasAmountValue = !isZero(amount.value)
const liqValid = gt(factor.value, '0') ? validateLiquidation(status.value, maxLiquidation.value) : null
return {
amount: { message: validateAmount(amountParsed.value, balance.value), show: hasAmountValue },
liquidation: { message: liqValid, show: hasAmountValue },
auth: { message: validateIsLoggedIn(!!account.value), show: true },
}
})
const { errorMessages, isValid } = useValidation(errors)
const pending = ref(false)
async function cast() {
pending.value = true
const amount = isMaxAmount.value ? dsa.value.maxValue : valInt(amountParsed.value, decimals.value)
const spells = dsa.value.Spell()
spells.add({
connector: 'compound',
method: 'deposit',
args: [tokenId.value, amount, 0, 0],
})
const txHash = await dsa.value.cast({
spells,
from: account.value,
})
showPendingTransaction(txHash)
pending.value = false
close()
}
return {
pending,
cast,
errors,
amount,
status,
rootTokenKey,
token,
symbol,
balance,
formatNumber,
formatUsdMax,
formatUsd,
toggle,
isMaxAmount,
maxLiquidation,
liquidationPrice,
liquidationMaxPrice,
errorMessages,
isValid
}
},
})
</script>

View File

@ -0,0 +1,212 @@
<template>
<SidebarContextRootContainer>
<template #title>Withdraw {{ symbol }}</template>
<SidebarSectionValueWithIcon label="Supplied" center>
<template #icon
><IconCurrency :currency="rootTokenKey" class="w-20 h-20" noHeight
/></template>
<template #value
>{{ formatNumber(originalBalance) }} {{ 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="maxLiquidation"
: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, 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 tokenIdMapping from '~/constant/tokenIdMapping'
import ctokens from '~/constant/ctokens'
import { useCompoundPosition } from '~/composables/useCompoundPosition'
export default defineComponent({
components: { InputNumeric, ToggleButton, ButtonCTA, Button },
props: {
tokenId: { type: String, required: true },
},
setup(props) {
const { close } = useSidebar()
const { networkName, account } = useWeb3()
const { dsa } = useDSA()
const { getTokenByKey, valInt } = useToken()
const { formatNumber, formatUsdMax, formatUsd } = useFormatting()
const { isZero, gt, plus, max, minus } = useBigNumber()
const { parseSafeFloat } = useParsing()
const { showPendingTransaction } = useNotification()
const originalBalance = ref('0')
const tokenId = computed(() => props.tokenId)
const tokenKey = computed(() => tokenIdMapping.idToToken[tokenId.value])
const rootTokenKey = computed(() => ctokens[networkName.value].rootTokens.includes(tokenKey.value) ? tokenKey.value : 'eth')
const { stats, status, displayPositions, maxLiquidation, liquidationPrice, liquidationMaxPrice } = useCompoundPosition({
overridePosition: (position) => {
if (tokenId.value !== position.cTokenId) return position
originalBalance.value = position.supply
return {
...position,
supply: max(minus(position.supply, amountParsed.value), '0').toFixed(),
}
},
})
const balance = computed(
() => displayPositions.value.find((position) => position.cTokenId === tokenId.value)?.supply || '0'
)
const amount = ref('')
const amountParsed = computed(() => parseSafeFloat(amount.value))
const token = computed(() => getTokenByKey(rootTokenKey.value))
const symbol = computed(() => token.value?.symbol)
const decimals = computed(() => token.value?.decimals)
const factor = computed(
() => displayPositions.value?.find((position) => position.cTokenId === tokenId.value)?.factor
)
const { toggle, isMaxAmount } = useMaxAmountActive(amount, balance)
const { validateAmount, validateLiquidation, validateIsLoggedIn, validateLiquidity } = useValidators()
const errors = computed(() => {
const hasAmountValue = !isZero(amount.value)
const liqValid = gt(factor.value, '0')
? validateLiquidation(status.value, maxLiquidation.value, isZero(stats.value.totalBorrowInEth))
: null
return {
amount: { message: validateAmount(amountParsed.value, originalBalance.value), show: hasAmountValue },
liquidation: { message: liqValid, show: hasAmountValue },
auth: { message: validateIsLoggedIn(!!account.value), show: true },
}
})
const { errorMessages, isValid } = useValidation(errors)
const pending = ref(false)
async function cast() {
pending.value = true
const amount = isMaxAmount.value ? dsa.value.maxValue : valInt(amountParsed.value, decimals.value)
const spells = dsa.value.Spell()
spells.add({
connector: 'compound',
method: 'withdraw',
args: [tokenId.value, amount, 0, 0],
})
const txHash = await dsa.value.cast({
spells,
from: account.value,
})
showPendingTransaction(txHash)
pending.value = false
close()
}
return {
pending,
cast,
errors,
amount,
status,
rootTokenKey,
token,
symbol,
originalBalance,
balance,
formatNumber,
formatUsdMax,
formatUsd,
toggle,
isMaxAmount,
maxLiquidation,
liquidationPrice,
liquidationMaxPrice,
errorMessages,
isValid,
displayPositions,
}
},
})
</script>

View File

@ -56,19 +56,14 @@ const totalBorrow = computed(() =>
const ethPriceInUsd = computed(() => position.value?.ethPriceInUsd);
const annualPercentageRateTypes = computed(() => [
{ label: "Variable", value: "variable", rateMode: 2 },
{ label: "Stable", value: "stable", rateMode: 1 }
]);
export function useCompoundPosition(
{ overridePosition } = { overridePosition: null }
) {
overridePosition = overridePosition || (pos => pos);
const { web3, chainId, networkName } = useWeb3();
const { web3, networkName } = useWeb3();
const { activeAccount } = useDSA();
const { getTokenByKey, allATokensV2 } = useToken();
const { getTokenByKey } = useToken();
const resolver = computed(() => addresses.mainnet.resolver.compound);
@ -126,11 +121,14 @@ export function useCompoundPosition(
const stats = computed(() =>
displayPositions.value.reduce(
(stats, { key, supply, borrow, priceInEth, factor, liquidation }) => {
(stats, { key, supply, borrow, priceInEth, factor }) => {
if (key === "eth") {
stats.ethSupplied = supply;
}
console.log(key, supply, borrow, priceInEth, factor);
stats.totalSupplyInEth = plus(
stats.totalSupplyInEth,
times(supply, priceInEth)
@ -143,10 +141,6 @@ export function useCompoundPosition(
stats.totalMaxBorrowLimitInEth,
times(supply, times(priceInEth, factor))
).toFixed();
stats.totalMaxLiquidationLimitInEth = plus(
stats.totalMaxLiquidationLimitInEth,
times(supply, times(priceInEth, liquidation))
).toFixed();
return stats;
},
@ -154,7 +148,6 @@ export function useCompoundPosition(
totalSupplyInEth: "0",
totalBorrowInEth: "0",
totalMaxBorrowLimitInEth: "0",
totalMaxLiquidationLimitInEth: "0",
ethSupplied: "0"
}
)
@ -178,12 +171,15 @@ export function useCompoundPosition(
return [];
}
const ctokenPosition = position.value.data.find(
x => x.cTokenId === ctoken.id
const ctokenPosition = overridePosition(
position.value.data.find(x => x.cTokenId === ctoken.id)
);
const p = getPositionOrDefaultPosition(token, ctokenPosition);
if (!ctokenPosition) {
return [];
}
const p = getPositionOrDefaultPosition(token, ctokenPosition);
if (gt(p.supply, "0") && gt(p.borrow, "0")) {
return [
{ ...p, type: "supply" },
@ -202,15 +198,14 @@ export function useCompoundPosition(
return false;
}
return true;
})
.map(overridePosition);
});
});
function getPositionOrDefaultPosition(token, p) {
if (!p) {
function getPositionOrDefaultPosition(token, pos) {
if (!pos) {
const defaultPosition = {
key: token?.key,
tokenId: `${token?.symbol}-A`,
tokenId: `${token.symbol}-A`,
ctknBalance: "0",
cTokenDecimals: "0",
cf: "0",
@ -234,44 +229,34 @@ export function useCompoundPosition(
return {
key: token?.key,
tokenId: p.cTokenId,
ctknBalance: p.ctknBalance,
cTokenDecimals: p.cTokenDecimals,
cf: p.factor,
supply: p.supply,
supplyUsd: times(p.supply, p.priceInUsd).toFixed(),
supplyRate: p.supplyRate,
supplyYield: p.supplyYield,
borrow: p.borrow,
borrowUsd: times(p.borrow, p.priceInUsd).toFixed(),
borrowRate: p.borrowRate,
borrowYield: p.borrowYield,
borrowEnabled: p.borrowEnabled,
type: getType(p),
tokenId: pos.cTokenId,
ctknBalance: pos.ctknBalance,
cTokenDecimals: pos.cTokenDecimals,
cf: pos.factor,
supply: pos.supply,
supplyUsd: times(pos.supply, pos.priceInUsd).toFixed(),
supplyRate: pos.supplyRate,
supplyYield: pos.supplyYield,
borrow: pos.borrow,
borrowUsd: times(pos.borrow, pos.priceInUsd).toFixed(),
borrowRate: pos.borrowRate,
borrowYield: pos.borrowYield,
borrowEnabled: pos.borrowEnabled,
type: getType(pos),
supplyRewardRate: times(
p.compSupplyApy,
pos.compSupplyApy,
rewardTokenPriceInUsd.value
).toFixed(),
borrowRewardRate: times(
p.compBorrowApy,
pos.compBorrowApy,
rewardTokenPriceInUsd.value
).toFixed(),
priceInUsd: p.priceInUsd
priceInUsd: pos.priceInUsd,
priceInEth: pos.priceInEth,
factor: pos.factor,
};
}
const maxLiquidation = computed(() => {
if (isZero(stats.value.totalSupplyInEth)) return "0";
return max(
div(
stats.value.totalMaxLiquidationLimitInEth,
stats.value.totalSupplyInEth
),
"0"
).toFixed();
});
const liquidationPrice = computed(() => {
if (isZero(stats.value.ethSupplied)) return "0";
@ -279,7 +264,7 @@ export function useCompoundPosition(
times(
div(
stats.value.totalBorrowInEth,
stats.value.totalMaxLiquidationLimitInEth
stats.value.totalMaxBorrowLimitInEth
),
ethPriceInUsd.value
),
@ -320,10 +305,8 @@ export function useCompoundPosition(
totalBorrow,
status,
liquidation,
maxLiquidation,
liquidationPrice,
liquidationMaxPrice: ethPriceInUsd,
annualPercentageRateTypes
liquidationMaxPrice: ethPriceInUsd
};
}

View File

@ -1,6 +1,5 @@
import { computed, ref } from "@nuxtjs/composition-api";
//@ts-ignore
import NetworksMismatchDialog from "~/components/modal/NetworksMismatchDialog";
import NetworksMismatchDialog from "~/components/modal/NetworksMismatchDialog.vue";
const modal = ref(null);
const props = ref({});

View File

@ -1,9 +1,7 @@
import { computed, onMounted, ref, watch } from "@nuxtjs/composition-api";
import { useLocalStorage } from "vue-composable";
//@ts-ignore
import MainnetSVG from "~/assets/icons/mainnet.svg?inline";
//@ts-ignore
import PolygonSVG from "~/assets/icons/polygon.svg?inline";
import { useModal } from "./useModal";
import { useNotification } from "./useNotification";

View File

@ -1,4 +1,3 @@
// @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";

View File

@ -9,21 +9,21 @@ import {
import { useDSA } from "./useDSA";
import { useWeb3 } from "./useWeb3";
//@ts-ignore
import SidebarAaveV2Supply from "~/components/sidebar/context/aaveV2/SidebarAaveV2Supply.vue";
//@ts-ignore
import SidebarAaveV2Withdraw from '~/components/sidebar/context/aaveV2/SidebarAaveV2Withdraw.vue'
//@ts-ignore
import SidebarAaveV2Borrow from '~/components/sidebar/context/aaveV2/SidebarAaveV2Borrow.vue'
//@ts-ignore
import SidebarAaveV2Payback from '~/components/sidebar/context/aaveV2/SidebarAaveV2Payback.vue'
//@ts-ignore
import SidebarOverview from '~/components/sidebar/context/overview/SidebarOverview.vue'
//@ts-ignore
import SidebarDepositOverview from '~/components/sidebar/context/SidebarDepositOverview.vue'
//@ts-ignore
import SidebarWithdraw from '~/components/sidebar/context/SidebarWithdraw.vue'
import SidebarCompoundWithdraw from '~/components/sidebar/context/compound/SidebarCompoundWithdraw.vue'
import SidebarCompoundSupply from '~/components/sidebar/context/compound/SidebarCompoundSupply.vue'
import SidebarCompoundBorrow from '~/components/sidebar/context/compound/SidebarCompoundBorrow.vue'
import SidebarCompoundPayback from '~/components/sidebar/context/compound/SidebarCompoundBorrow.vue'
const sidebars = {
"#overview" : {component: SidebarOverview, back : false, close : true },
"#deposit-overview": {component: SidebarDepositOverview, back: { hash: 'overview' } },
@ -33,10 +33,6 @@ const sidebars = {
"/polygon/aave-v2#borrow": { component: SidebarAaveV2Borrow },
"/polygon/aave-v2#payback": { component: SidebarAaveV2Payback },
"/polygon/aave-v2#withdraw": { component: SidebarAaveV2Withdraw },
"/polygon/aave-v2#withdraw-token": {
component: null,
back: { hash: "withdraw-overview" }
},
"/mainnet/aave-v2": { component: null },
@ -44,10 +40,13 @@ const sidebars = {
"/mainnet/aave-v2#borrow": { component: SidebarAaveV2Borrow },
"/mainnet/aave-v2#payback": { component: SidebarAaveV2Payback },
"/mainnet/aave-v2#withdraw": { component: SidebarAaveV2Withdraw },
"/mainnet/aave-v2#withdraw-token": {
component: null,
back: { hash: "withdraw-overview" }
}
"/mainnet/compound": { component: null },
"/mainnet/compound#withdraw": { component: SidebarCompoundWithdraw },
"/mainnet/compound#supply": { component: SidebarCompoundSupply },
"/mainnet/compound#borrow": { component: SidebarCompoundBorrow },
"/mainnet/compound#payback": { component: SidebarCompoundPayback },
};
const sidebar = ref(null);

View File

@ -33,8 +33,9 @@
<script lang="ts">
import { computed, defineComponent } from "@nuxtjs/composition-api";
import { useNetwork } from "~/composables/useNetwork";
//@ts-ignore
import AaveIcon from "~/assets/icons/colored/aave.svg?inline";
import CompoundIcon from "~/assets/icons/colored/compound.svg?inline";
import MakerIcon from "~/assets/icons/colored/maker.svg?inline";
const appsPerNetwork = {
mainnet: [
@ -47,14 +48,14 @@ const appsPerNetwork = {
},
{
id: "compound",
icon: AaveIcon,
icon: CompoundIcon,
name: "Compound",
url: "/mainnet/compound",
description: "Lend and borrow straight from your Gnosis Safe"
},
{
id: "maker",
icon: AaveIcon,
icon: MakerIcon,
name: "Maker",
url: "/mainnet/maker",
description: "Lend and borrow straight from your Gnosis Safe"

View File

@ -148,7 +148,6 @@
<script lang="ts">
import { defineComponent, computed } from "@nuxtjs/composition-api";
// @ts-ignore
import BackIcon from "~/assets/icons/back.svg?inline";
import { useAaveV2Position } from "~/composables/useAaveV2Position";
import { useFormatting } from "~/composables/useFormatting";

View File

@ -146,7 +146,6 @@
<script lang="ts">
import { defineComponent, computed } from "@nuxtjs/composition-api";
// @ts-ignore
import BackIcon from "~/assets/icons/back.svg?inline";
import { useCompoundPosition } from "~/composables/useCompoundPosition";
import { useFormatting } from "~/composables/useFormatting";

View File

@ -148,7 +148,6 @@
<script lang="ts">
import { defineComponent, computed } from "@nuxtjs/composition-api";
// @ts-ignore
import BackIcon from "~/assets/icons/back.svg?inline";
import { useAaveV2Position } from "~/composables/useAaveV2Position";
import { useFormatting } from "~/composables/useFormatting";

9
shims.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
declare module "*.vue" {
import Vue from 'vue'
export default Vue
}
declare module "*.svg?inline" {
const content: any;
export default content;
}