mirror of
				https://github.com/Instadapp/assembly.git
				synced 2024-07-29 22:37:06 +00:00 
			
		
		
		
	Aave v2 Borrow & Withdraw
This commit is contained in:
		
							parent
							
								
									19f634e50d
								
							
						
					
					
						commit
						99336e1bb6
					
				
							
								
								
									
										18
									
								
								components/Alert.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								components/Alert.vue
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div class="p-2 bg-red-400 rounded-sm bg-opacity-17">
 | 
			
		||||
    <div class="flex">
 | 
			
		||||
      <div class="flex-shrink-0">
 | 
			
		||||
        <Icon name="x-circle" type="solid" class="w-5 h-5 text-red-500" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="ml-2">
 | 
			
		||||
        <slot />
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { defineComponent } from '@nuxtjs/composition-api'
 | 
			
		||||
 | 
			
		||||
export default defineComponent({})
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <Card class="flex w-full pl-4 overflow-hidden border border-opacity-75 shadow-lg dark:bg-dark-300 border-grey-light">
 | 
			
		||||
  <div class="flex w-full pl-4 overflow-hidden border border-opacity-75 shadow-lg dark:bg-dark-300 border-grey-light">
 | 
			
		||||
    <div class="flex items-center w-full py-4">
 | 
			
		||||
      <IconNotification :icon="icon" />
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +35,7 @@
 | 
			
		|||
        <Icon name="x" class="w-full" />
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </Card>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ export default defineComponent({
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    onMounted(() => {
 | 
			
		||||
      if (props.duration) {
 | 
			
		||||
      if (props && props.duration) {
 | 
			
		||||
        setTimeout(dismiss, props.duration)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										46
									
								
								components/common/input/ButtonBullet.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								components/common/input/ButtonBullet.vue
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div
 | 
			
		||||
    class="flex items-center cursor-pointer group"
 | 
			
		||||
    @click="$emit('change', value)"
 | 
			
		||||
  >
 | 
			
		||||
    <div
 | 
			
		||||
      class="flex items-center justify-center w-5 h-5 duration-150 border rounded-full transition-color group-hover:border-ocean-blue-pure dark:group-hover:border-light"
 | 
			
		||||
      :class="{
 | 
			
		||||
        'border-ocean-blue-pure dark:border-light': checked,
 | 
			
		||||
        'border-grey-pure': !checked
 | 
			
		||||
      }"
 | 
			
		||||
    >
 | 
			
		||||
      <transition-fade-in>
 | 
			
		||||
        <div
 | 
			
		||||
          v-if="checked"
 | 
			
		||||
          class="w-2 h-2 rounded-full bg-ocean-blue-pure dark:bg-light"
 | 
			
		||||
        ></div>
 | 
			
		||||
      </transition-fade-in>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-if="label" :class="labelClasses">{{ label }}</div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { defineComponent } from '@nuxtjs/composition-api'
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  props: {
 | 
			
		||||
    value: {},
 | 
			
		||||
    checked: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: false,
 | 
			
		||||
    },
 | 
			
		||||
    label: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: '',
 | 
			
		||||
    },
 | 
			
		||||
    labelClasses: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: 'ml-2 font-medium',
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style></style>
 | 
			
		||||
							
								
								
									
										233
									
								
								components/sidebar/context/aaveV2/SidebarAaveV2Borrow.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								components/sidebar/context/aaveV2/SidebarAaveV2Borrow.vue
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,233 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <SidebarContextRootContainer>
 | 
			
		||||
    <template #title>Borrow {{ symbol }}</template>
 | 
			
		||||
 | 
			
		||||
    <SidebarRateTypeSelect
 | 
			
		||||
      class="flex flex-col items-center"
 | 
			
		||||
      v-model="rateType"
 | 
			
		||||
      :items="annualPercentageRateTypes"
 | 
			
		||||
      :borrow-stable-rate="borrowStableRate"
 | 
			
		||||
      :stable-borrow-enabled="stableBorrowEnabled"
 | 
			
		||||
    />
 | 
			
		||||
 | 
			
		||||
    <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 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"
 | 
			
		||||
        >
 | 
			
		||||
          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 { useAaveV2Position } from '~/composables/useAaveV2Position'
 | 
			
		||||
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 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 { useNotification } from '~/composables/useNotification'
 | 
			
		||||
import Button from '~/components/Button.vue'
 | 
			
		||||
import { useSidebar } from '~/composables/useSidebar'
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  components: { InputNumeric, ToggleButton, ButtonCTA, Button },
 | 
			
		||||
  props: {
 | 
			
		||||
    tokenKey: { 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 { status, displayPositions, liquidation, maxLiquidation, liquidationPrice, liquidationMaxPrice, annualPercentageRateTypes } = useAaveV2Position({
 | 
			
		||||
      overridePosition: (position) => {
 | 
			
		||||
        if (rootTokenKey.value !== position.key) return position
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
          ...position,
 | 
			
		||||
          borrow: plus(position.borrow, amountParsed.value).toFixed(),
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const rateType = ref(null)
 | 
			
		||||
 | 
			
		||||
    const amount = ref('')
 | 
			
		||||
    const amountParsed = computed(() => parseSafeFloat(amount.value))
 | 
			
		||||
 | 
			
		||||
    const rootTokenKey = computed(() => atokens[networkName.value].rootTokens.includes(props.tokenKey) ? props.tokenKey : 'eth')
 | 
			
		||||
 | 
			
		||||
    const currentPosition = computed(() =>
 | 
			
		||||
      displayPositions.value.find((position) => position.key === rootTokenKey.value)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    const token = computed(() => getTokenByKey(rootTokenKey.value))
 | 
			
		||||
    const symbol = computed(() => token.value?.symbol)
 | 
			
		||||
    const decimals = computed(() => token.value?.decimals)
 | 
			
		||||
    const balance = computed(() => {
 | 
			
		||||
      if (rateType.value?.value === 'stable') {
 | 
			
		||||
        return currentPosition.value?.borrowStable || '0'
 | 
			
		||||
      }
 | 
			
		||||
      return currentPosition.value?.borrow || '0'
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const availableLiquidity = computed(() => currentPosition.value?.availableLiquidity || '0')
 | 
			
		||||
    const borrowStableRate = computed(() => currentPosition.value?.borrowStableRate || '0')
 | 
			
		||||
    const stableBorrowEnabled = computed(
 | 
			
		||||
      () => currentPosition.value?.stableBorrowEnabled && isZero(currentPosition.value?.supply)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    const address = computed(() => token.value?.address)
 | 
			
		||||
 | 
			
		||||
    const factor = computed(
 | 
			
		||||
      () => displayPositions.value?.find((position) => rootTokenKey.value === position.key)?.factor
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    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 },
 | 
			
		||||
        liquidity: {
 | 
			
		||||
          message: validateLiquidity(amountParsed.value, availableLiquidity.value, symbol.value),
 | 
			
		||||
          show: hasAmountValue,
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    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()
 | 
			
		||||
 | 
			
		||||
      const rateMode = rateType.value?.rateMode
 | 
			
		||||
 | 
			
		||||
      spells.add({
 | 
			
		||||
        connector: 'aave_v2',
 | 
			
		||||
        method: 'borrow',
 | 
			
		||||
        args: [address.value, amount, rateMode, 0, 0],
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const txHash = await dsa.value.cast({
 | 
			
		||||
        spells,
 | 
			
		||||
        from: account.value,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      fetchBalances(true)
 | 
			
		||||
 | 
			
		||||
      pending.value = false
 | 
			
		||||
 | 
			
		||||
      close()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      pending,
 | 
			
		||||
      cast,
 | 
			
		||||
      errors,
 | 
			
		||||
      amount,
 | 
			
		||||
      status,
 | 
			
		||||
      rootTokenKey,
 | 
			
		||||
      token,
 | 
			
		||||
      symbol,
 | 
			
		||||
      balance,
 | 
			
		||||
      formatNumber,
 | 
			
		||||
      formatUsdMax,
 | 
			
		||||
      formatUsd,
 | 
			
		||||
      toggle,
 | 
			
		||||
      isMaxAmount,
 | 
			
		||||
      maxLiquidation,
 | 
			
		||||
      liquidationPrice,
 | 
			
		||||
      liquidationMaxPrice,
 | 
			
		||||
      errorMessages,
 | 
			
		||||
      isValid,
 | 
			
		||||
      annualPercentageRateTypes,
 | 
			
		||||
      availableLiquidity,
 | 
			
		||||
      borrowStableRate,
 | 
			
		||||
      stableBorrowEnabled,
 | 
			
		||||
      rateType,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										247
									
								
								components/sidebar/context/aaveV2/SidebarAaveV2Payback.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								components/sidebar/context/aaveV2/SidebarAaveV2Payback.vue
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,247 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <SidebarContextRootContainer>
 | 
			
		||||
    <template #title>Payback {{ symbol }}</template>
 | 
			
		||||
 | 
			
		||||
    <SidebarRateTypeSelect
 | 
			
		||||
      class="flex flex-col items-center"
 | 
			
		||||
      v-model="rateType"
 | 
			
		||||
      :items="annualPercentageRateTypes"
 | 
			
		||||
      :borrow-stable-rate="borrowStableRate"
 | 
			
		||||
      :stable-borrow-enabled="stableBorrowEnabled"
 | 
			
		||||
    />
 | 
			
		||||
 | 
			
		||||
    <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>
 | 
			
		||||
 | 
			
		||||
    <SidebarSectionValueWithIcon class="" label="Token Balance" center>
 | 
			
		||||
      <template #value
 | 
			
		||||
        >{{ formatNumber(tokenMaxBalance) }} {{ 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"
 | 
			
		||||
        >
 | 
			
		||||
          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 { useAaveV2Position } from '~/composables/useAaveV2Position'
 | 
			
		||||
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 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 { useNotification } from '~/composables/useNotification'
 | 
			
		||||
import Button from '~/components/Button.vue'
 | 
			
		||||
import { useSidebar } from '~/composables/useSidebar'
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  components: { InputNumeric, ToggleButton, ButtonCTA, Button },
 | 
			
		||||
  props: {
 | 
			
		||||
    tokenKey: { 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 { status, displayPositions, liquidation, maxLiquidation, liquidationPrice, liquidationMaxPrice, annualPercentageRateTypes } = useAaveV2Position({
 | 
			
		||||
      overridePosition: (position) => {
 | 
			
		||||
        if (rootTokenKey.value !== position.key) return position
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
          ...position,
 | 
			
		||||
          borrow: max(minus(position.borrow, amountParsed.value), '0').toFixed(),
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const rateType = ref(null)
 | 
			
		||||
 | 
			
		||||
    const amount = ref('')
 | 
			
		||||
    const amountParsed = computed(() => parseSafeFloat(amount.value))
 | 
			
		||||
 | 
			
		||||
    const rootTokenKey = computed(() => atokens[networkName.value].rootTokens.includes(props.tokenKey) ? props.tokenKey : 'eth')
 | 
			
		||||
 | 
			
		||||
    const currentPosition = computed(() =>
 | 
			
		||||
      displayPositions.value.find((position) => position.key === rootTokenKey.value)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    const token = computed(() => getTokenByKey(rootTokenKey.value))
 | 
			
		||||
    const symbol = computed(() => token.value?.symbol)
 | 
			
		||||
    const decimals = computed(() => token.value?.decimals)
 | 
			
		||||
    const balance = computed(() => {
 | 
			
		||||
      if (rateType.value?.value === 'stable') {
 | 
			
		||||
        return currentPosition.value?.borrowStable || '0'
 | 
			
		||||
      }
 | 
			
		||||
      return currentPosition.value?.borrow || '0'
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const tokenMaxBalance = computed(() => getBalanceByKey(rootTokenKey.value))
 | 
			
		||||
    const tokenMaxBalanceRaw = computed(() => getBalanceRawByKey(rootTokenKey.value))
 | 
			
		||||
 | 
			
		||||
    const availableLiquidity = computed(() => currentPosition.value?.availableLiquidity || '0')
 | 
			
		||||
    const borrowStableRate = computed(() => currentPosition.value?.borrowStableRate || '0')
 | 
			
		||||
    const stableBorrowEnabled = computed(
 | 
			
		||||
      () => currentPosition.value?.stableBorrowEnabled && isZero(currentPosition.value?.supply)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    const address = computed(() => token.value?.address)
 | 
			
		||||
 | 
			
		||||
    const factor = computed(
 | 
			
		||||
      () => displayPositions.value?.find((position) => rootTokenKey.value === position.key)?.factor
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    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 },
 | 
			
		||||
        liquidity: {
 | 
			
		||||
          message: validateLiquidity(amountParsed.value, availableLiquidity.value, symbol.value),
 | 
			
		||||
          show: hasAmountValue,
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    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: 'aave_v2',
 | 
			
		||||
        method: 'payback',
 | 
			
		||||
        args: [address.value, amount, rateMode, 0, 0],
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const txHash = await dsa.value.cast({
 | 
			
		||||
        spells,
 | 
			
		||||
        from: account.value,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      fetchBalances(true)
 | 
			
		||||
 | 
			
		||||
      pending.value = false
 | 
			
		||||
 | 
			
		||||
      close()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      pending,
 | 
			
		||||
      cast,
 | 
			
		||||
      errors,
 | 
			
		||||
      amount,
 | 
			
		||||
      status,
 | 
			
		||||
      rootTokenKey,
 | 
			
		||||
      token,
 | 
			
		||||
      symbol,
 | 
			
		||||
      balance,
 | 
			
		||||
      formatNumber,
 | 
			
		||||
      formatUsdMax,
 | 
			
		||||
      formatUsd,
 | 
			
		||||
      toggle,
 | 
			
		||||
      isMaxAmount,
 | 
			
		||||
      maxLiquidation,
 | 
			
		||||
      liquidationPrice,
 | 
			
		||||
      liquidationMaxPrice,
 | 
			
		||||
      errorMessages,
 | 
			
		||||
      isValid,
 | 
			
		||||
      annualPercentageRateTypes,
 | 
			
		||||
      availableLiquidity,
 | 
			
		||||
      borrowStableRate,
 | 
			
		||||
      stableBorrowEnabled,
 | 
			
		||||
      rateType,
 | 
			
		||||
      tokenMaxBalance,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +82,6 @@ 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 { useNotification } from '~/composables/useNotification'
 | 
			
		||||
import Button from '~/components/Button.vue'
 | 
			
		||||
import { useSidebar } from '~/composables/useSidebar'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,11 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="flex items-center">
 | 
			
		||||
      <ValueDisplayLabel class="leading-none">APR type</ValueDisplayLabel>
 | 
			
		||||
      <Info class="ml-2" :text="tooltipARP" />
 | 
			
		||||
      <value-display-label class="leading-none">APR type</value-display-label>
 | 
			
		||||
      <info class="ml-2" :text="tooltipARP" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-if="stableBorrowEnabled" class="flex mt-2">
 | 
			
		||||
      <ButtonBullet
 | 
			
		||||
      <button-bullet
 | 
			
		||||
        v-for="item in items"
 | 
			
		||||
        :key="item.value"
 | 
			
		||||
        :value="value"
 | 
			
		||||
| 
						 | 
				
			
			@ -21,9 +21,13 @@
 | 
			
		|||
 | 
			
		||||
<script>
 | 
			
		||||
import { defineComponent, onMounted } from '@nuxtjs/composition-api'
 | 
			
		||||
import ButtonBullet from '~/components/common/input/ButtonBullet.vue'
 | 
			
		||||
import Info from '~/components/Info.vue'
 | 
			
		||||
import { useFormatting } from '~/composables/useFormatting'
 | 
			
		||||
import ValueDisplayLabel from './ValueDisplayLabel.vue'
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  components: { ValueDisplayLabel, ButtonBullet, Info },
 | 
			
		||||
  props: {
 | 
			
		||||
    value: { type: Object, default: () => {} },
 | 
			
		||||
    items: { type: Array, default: () => [] },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,11 @@ 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 useAaveV2Position(
 | 
			
		||||
  { overridePosition } = { overridePosition: null }
 | 
			
		||||
) {
 | 
			
		||||
| 
						 | 
				
			
			@ -305,6 +310,15 @@ export function useAaveV2Position(
 | 
			
		|||
    ).toFixed();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const liquidation = computed(() => {
 | 
			
		||||
    if (isZero(stats.value.totalSupplyInEth)) return "0";
 | 
			
		||||
 | 
			
		||||
    return max(
 | 
			
		||||
      div(stats.value.totalMaxBorrowLimitInEth, stats.value.totalSupplyInEth),
 | 
			
		||||
      "0"
 | 
			
		||||
    ).toFixed();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    stats,
 | 
			
		||||
    displayPositions,
 | 
			
		||||
| 
						 | 
				
			
			@ -313,9 +327,11 @@ export function useAaveV2Position(
 | 
			
		|||
    totalSupply,
 | 
			
		||||
    totalBorrow,
 | 
			
		||||
    status,
 | 
			
		||||
    liquidation,
 | 
			
		||||
    maxLiquidation,
 | 
			
		||||
    liquidationPrice,
 | 
			
		||||
    liquidationMaxPrice: ethPriceInUsd
 | 
			
		||||
    liquidationMaxPrice: ethPriceInUsd,
 | 
			
		||||
    annualPercentageRateTypes
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,10 +56,19 @@ export function useBalances() {
 | 
			
		|||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getBalanceRawByKey = (tokenKey, network = null) => {
 | 
			
		||||
    return (
 | 
			
		||||
      balances.dsa?.[network || networkName.value][
 | 
			
		||||
        getTokenByKey(tokenKey)?.address
 | 
			
		||||
      ]?.raw || "0"
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  return {
 | 
			
		||||
    balances,
 | 
			
		||||
    fetchBalances,
 | 
			
		||||
    getBalanceByKey
 | 
			
		||||
    getBalanceByKey,
 | 
			
		||||
    getBalanceRawByKey
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,5 +17,6 @@ export function useLink() {
 | 
			
		|||
 | 
			
		||||
export const getEtherscanLink = (transactionHash) => `https://etherscan.io/tx/${transactionHash}`
 | 
			
		||||
export const getMaticLink = (transactionHash) => `https://polygonscan.com/tx/${transactionHash}`
 | 
			
		||||
export const getPolygonLink = (transactionHash) => `https://polygonscan.com/tx/${transactionHash}`
 | 
			
		||||
export const getTenderlyLink = (simulationId) =>
 | 
			
		||||
  `https://dashboard.tenderly.co/public/InstaDApp/dsa-simulations/fork-simulation/${simulationId}?hideSidebar=true`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import NetworksMismatchDialog from "~/components/modal/NetworksMismatchDialog";
 | 
			
		||||
import { computed, ref } from "@nuxtjs/composition-api";
 | 
			
		||||
//@ts-ignore
 | 
			
		||||
import NetworksMismatchDialog from "~/components/modal/NetworksMismatchDialog";
 | 
			
		||||
 | 
			
		||||
const modal = ref(null);
 | 
			
		||||
const props = ref({});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,13 +14,13 @@ export enum Network {
 | 
			
		|||
  Polygon = "polygon"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const networks = [
 | 
			
		||||
export const networks = [
 | 
			
		||||
  { id: "mainnet", chainId: 1, name: "Mainnet", icon: MainnetSVG },
 | 
			
		||||
  { id: "polygon", chainId: 136, name: "Polygon", icon: PolygonSVG }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const activeNetworkId = ref<Network>(Network.Polygon);
 | 
			
		||||
const activeNetwork = computed(
 | 
			
		||||
export const activeNetworkId = ref<Network>(Network.Polygon);
 | 
			
		||||
export const activeNetwork = computed(
 | 
			
		||||
  () => networks.find(n => n.id === activeNetworkId.value) || networks[0]
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ import { ref } from "@nuxtjs/composition-api";
 | 
			
		|||
import { useFormatting } from "@/composables/useFormatting";
 | 
			
		||||
import { getEtherscanLink, getMaticLink, getTenderlyLink } from "./useLink";
 | 
			
		||||
import { useRandom } from "./useRandom";
 | 
			
		||||
import { Network, activeNetworkId } from "./useNetwork";
 | 
			
		||||
const { makeid } = useRandom();
 | 
			
		||||
 | 
			
		||||
const queue = ref([]);
 | 
			
		||||
| 
						 | 
				
			
			@ -85,9 +86,11 @@ export function useNotification() {
 | 
			
		|||
    return key;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function showPendingTransaction(transactionHash, network) {
 | 
			
		||||
  function showPendingTransaction(transactionHash, network?: Network) {
 | 
			
		||||
    network = network || activeNetworkId.value;
 | 
			
		||||
 | 
			
		||||
    let href;
 | 
			
		||||
    if (network === "matic") {
 | 
			
		||||
    if (network === Network.Polygon) {
 | 
			
		||||
      href = getMaticLink(transactionHash);
 | 
			
		||||
    } else {
 | 
			
		||||
      href = getEtherscanLink(transactionHash);
 | 
			
		||||
| 
						 | 
				
			
			@ -104,9 +107,11 @@ export function useNotification() {
 | 
			
		|||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function showConfirmedTransaction(transactionHash, network) {
 | 
			
		||||
  function showConfirmedTransaction(transactionHash, network?: Network) {
 | 
			
		||||
    network = network || activeNetworkId.value;
 | 
			
		||||
 | 
			
		||||
    let href;
 | 
			
		||||
    if (network === "matic") {
 | 
			
		||||
    if (network === Network.Polygon) {
 | 
			
		||||
      href = getMaticLink(transactionHash);
 | 
			
		||||
    } else {
 | 
			
		||||
      href = getEtherscanLink(transactionHash);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,17 +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'
 | 
			
		||||
// import SidebarAaveV2Borrow from '~/components/sidebar/context/aaveV2/SidebarAaveV2Borrow.vue'
 | 
			
		||||
// import SidebarAaveV2Payback from '~/components/sidebar/context/aaveV2/SidebarAaveV2Payback.vue'
 | 
			
		||||
//@ts-ignore
 | 
			
		||||
import SidebarAaveV2Borrow from '~/components/sidebar/context/aaveV2/SidebarAaveV2Borrow.vue'
 | 
			
		||||
//@ts-ignore
 | 
			
		||||
import SidebarAaveV2Payback from '~/components/sidebar/context/aaveV2/SidebarAaveV2Payback.vue'
 | 
			
		||||
 | 
			
		||||
const sidebars = {
 | 
			
		||||
  "/polygon/aave-v2": { component: null },
 | 
			
		||||
  "/polygon/aave-v2#overview": { component: null },
 | 
			
		||||
  "/polygon/aave-v2#supply": { component: SidebarAaveV2Supply },
 | 
			
		||||
  "/polygon/aave-v2#borrow": { component: null },
 | 
			
		||||
  "/polygon/aave-v2#payback": { component: null },
 | 
			
		||||
  "/polygon/aave-v2#borrow": { component: SidebarAaveV2Borrow },
 | 
			
		||||
  "/polygon/aave-v2#payback": { component: SidebarAaveV2Payback },
 | 
			
		||||
  "/polygon/aave-v2#withdraw": { component: SidebarAaveV2Withdraw },
 | 
			
		||||
  "/polygon/aave-v2#withdraw-token": {
 | 
			
		||||
    component: null,
 | 
			
		||||
| 
						 | 
				
			
			@ -30,8 +34,8 @@ const sidebars = {
 | 
			
		|||
  "/mainnet/aave-v2": { component: null },
 | 
			
		||||
  "/mainnet/aave-v2#overview": { component: null },
 | 
			
		||||
  "/mainnet/aave-v2#supply": { component: SidebarAaveV2Supply },
 | 
			
		||||
  "/mainnet/aave-v2#borrow": { component: null },
 | 
			
		||||
  "/mainnet/aave-v2#payback": { component: null },
 | 
			
		||||
  "/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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -109,7 +109,10 @@ module.exports = {
 | 
			
		|||
      },
 | 
			
		||||
      fontFamily: {
 | 
			
		||||
        sans: ["Montserrat", ...defaultTheme.fontFamily.sans]
 | 
			
		||||
      }
 | 
			
		||||
      },
 | 
			
		||||
      animation: {
 | 
			
		||||
        'spin-loading': 'spin 1s cubic-bezier(.6,0,.4,1) infinite',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  variants: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user