mirror of
https://github.com/Instadapp/assembly.git
synced 2024-07-29 22:37:06 +00:00
Merge branch 'master' into simulation
This commit is contained in:
commit
83d7f54797
27
.github/workflows/assembly-production-deployment-automation.yml
vendored
Normal file
27
.github/workflows/assembly-production-deployment-automation.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
name: production-deployment-automation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Deploy to Server
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.REMOTE_HOST }}
|
||||
username: ${{ secrets.REMOTE_USER }}
|
||||
key: ${{ secrets.SERVER_SSH_KEY }}
|
||||
port: 22
|
||||
script: |
|
||||
cd ${{ secrets.REMOTE_TARGET }} && \
|
||||
git reset --hard && git clean -df && \
|
||||
git pull && \
|
||||
npm i && \
|
||||
npm run build --if-present && \
|
||||
npm run generate --if-present && \
|
||||
pm2 restart assembly --update-env
|
26
assets/icons/1inch.svg
Normal file
26
assets/icons/1inch.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
85
components/common/input/InputPercent.vue
Normal file
85
components/common/input/InputPercent.vue
Normal file
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<div class="flex flex-col flex-shrink-0 w-full">
|
||||
<div class="relative rounded-sm">
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="w-full pl-4 pr-6 py-2 rounded-[6px] border border-grey-dark border-opacity-[0.15]"
|
||||
:class="{ 'text-right': right, 'bg-primary-gray/[.15] text-primary-gray': $attrs.disabled }"
|
||||
type="text"
|
||||
inputmode="decimal"
|
||||
:value="value"
|
||||
v-bind="$attrs"
|
||||
v-on="inputListeners"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="!$attrs.disabled"
|
||||
class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none select-none"
|
||||
>
|
||||
<span
|
||||
class="font-semibold text-ocean-blue-pure text-11 dark:text-light"
|
||||
>
|
||||
%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-0">
|
||||
<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"
|
||||
class="mt-1 text-red-600 text-11"
|
||||
>
|
||||
{{ error }}
|
||||
</p>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, watch, ref } from '@nuxtjs/composition-api'
|
||||
import { useInputListeners } from '@/composables/useInputListeners'
|
||||
import { usePattern } from '@/composables/usePattern'
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: { type: String, default: '0' },
|
||||
showError: { type: Boolean, default: false },
|
||||
error: { type: String, default: null },
|
||||
max: {
|
||||
type: String,
|
||||
default: '100',
|
||||
},
|
||||
right: { type: Boolean, default: false },
|
||||
},
|
||||
|
||||
setup(props, context) {
|
||||
const { amountPattern } = usePattern()
|
||||
const amountFilter = (value) => amountPattern.test(value)
|
||||
|
||||
const params = { max: props.max }
|
||||
|
||||
const { inputListeners } = useInputListeners(props, context, amountFilter, params)
|
||||
|
||||
const touched = ref(false)
|
||||
const stopTouchedWatcher = watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
touched.value = true
|
||||
stopTouchedWatcher()
|
||||
}
|
||||
)
|
||||
|
||||
return { inputListeners, touched }
|
||||
},
|
||||
})
|
||||
</script>
|
|
@ -11,10 +11,10 @@
|
|||
@click="toggle"
|
||||
>
|
||||
<div
|
||||
class="box-content flex w-10 h-5 transition-colors duration-75 rounded-full p-0.5 shadow-inner"
|
||||
class="box-content flex w-10 h-5 transition-colors duration-75 rounded-full p-0.5"
|
||||
:class="{
|
||||
'bg-green-pure': checked,
|
||||
'bg-grey-light group-hover:bg-grey-pure group-hover:bg-opacity-20 focus:bg-grey-pure focus:bg-opacity-20': !checked
|
||||
'bg-primary-blue-dark': checked,
|
||||
'bg-grey-light group-hover:bg-grey-pure group-hover:bg-opacity-20 focus:bg-grey-pure focus:bg-opacity-20 dark:group-hover:bg-opacity-38 dark:bg-opacity-20 ': !checked
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
|
@ -22,7 +22,7 @@
|
|||
style="top: calc(50% - 0.5rem)"
|
||||
:style="{ transform }"
|
||||
>
|
||||
<spinner v-if="loading" class="text-green-pure" />
|
||||
<spinner v-if="loading" class="text-primary-blue-dark" />
|
||||
</div>
|
||||
<span v-if="label" class="ml-2 font-semibold text-primary-gray">{{ label }}</span>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,13 @@
|
|||
<Spinner class="w-6 h-6" />
|
||||
</div>
|
||||
<div v-else class="flex flex-col flex-grow">
|
||||
<div v-if="items.length" :class="{ ' divide-y divide-grey-light divide-opacity-50': divided }">
|
||||
<div
|
||||
v-if="items.length"
|
||||
:class="[
|
||||
itemsWrapperClasses,
|
||||
{ ' divide-y divide-grey-light divide-opacity-50': divided }
|
||||
]"
|
||||
>
|
||||
<div v-for="(item, i) in items" :key="i">
|
||||
<slot name="default" :item="item" :index="i"></slot>
|
||||
</div>
|
||||
|
@ -27,6 +33,10 @@ export default defineComponent({
|
|||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
itemsWrapperClasses: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
divided: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<div
|
||||
v-close-popover="closeOnClick"
|
||||
:border-radius="borderRadius"
|
||||
class="flex overflow-hidden border border-opacity-50 shadow-lg border-grey-light dark:bg-dark-400"
|
||||
class="flex overflow-hidden border border-opacity-50 shadow-lg border-grey-light"
|
||||
:class="{ 'py-2': !noPadding }"
|
||||
>
|
||||
<slot />
|
||||
|
|
496
components/swap/SwapCard.vue
Normal file
496
components/swap/SwapCard.vue
Normal file
|
@ -0,0 +1,496 @@
|
|||
<template>
|
||||
<div>
|
||||
<h3 class="text-primary-gray text-lg font-semibold mb-4">Swap</h3>
|
||||
<div class="relative">
|
||||
<div
|
||||
class="shadow-sm relative border border-grey-dark/[0.15] rounded-lg p-4 mb-2"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex-1" @click="() => token0Input.focus()">
|
||||
<input
|
||||
type="text"
|
||||
v-model.trim="token0.amount"
|
||||
ref="token0Input"
|
||||
inputmode="decimal"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
pattern="^[0-9]*[.,]?[0-9]*$"
|
||||
placeholder="0.0"
|
||||
minlength="1"
|
||||
maxlength="79"
|
||||
spellcheck="false"
|
||||
:size="
|
||||
String(token0.amount).length > 0
|
||||
? Math.min(String(token0.amount).length, 24)
|
||||
: 2
|
||||
"
|
||||
class="border-0 focus:outline-none focus:ring-0 text-lg font-semibold px-0 max-w-full"
|
||||
/>
|
||||
<span class="text-primary-gray text-lg font-semibold">{{
|
||||
token0.symbol
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<Dropdown>
|
||||
<template #trigger="{ toggle }">
|
||||
<button
|
||||
class="flex items-center hover:bg-primary-blue-dark/10 p-1.5 rounded"
|
||||
@click="toggle"
|
||||
>
|
||||
<svg
|
||||
width="9"
|
||||
height="6"
|
||||
viewBox="0 0 9 6"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4.5 4.5L3.89896 5.10104C4.2309 5.43299 4.76909 5.43299 5.10104 5.10104L4.5 4.5ZM8.60104 1.60104C8.93299 1.2691 8.93299 0.730905 8.60104 0.398959C8.2691 0.0670136 7.7309 0.0670135 7.39896 0.398959L8.60104 1.60104ZM1.60104 0.398959C1.26909 0.0670133 0.730905 0.0670132 0.398959 0.398959C0.0670138 0.730905 0.0670137 1.26909 0.398959 1.60104L1.60104 0.398959ZM5.10104 5.10104L8.60104 1.60104L7.39896 0.398959L3.89896 3.89896L5.10104 5.10104ZM5.10104 3.89896L1.60104 0.398959L0.398959 1.60104L3.89896 5.10104L5.10104 3.89896Z"
|
||||
fill="#3F75FF"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<icon-currency
|
||||
:currency="token0.symbol.toLowerCase()"
|
||||
class="ml-2.5 w-9 h-9"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
<template #menu="{ close }">
|
||||
<DropdownMenu
|
||||
class="z-10 bg-white w-32 rounded border border-[#CFDCFF] drop-shadow-sm pt-0 pb-0"
|
||||
align="right"
|
||||
>
|
||||
<List
|
||||
:items="allTokens.filter(t => t.symbol != token1.symbol)"
|
||||
class="p-1.5"
|
||||
items-wrapper-classes="space-y-2 overflow-y-scroll max-h-[200px]"
|
||||
>
|
||||
<template v-slot:default="{ item }">
|
||||
<div
|
||||
@click="
|
||||
selectToken0(item);
|
||||
close();
|
||||
"
|
||||
class="cursor-pointer rounded-sm flex items-center p-1.5 bg-[#F6F7F9] border border-transparent hover:border-primary-blue-dark"
|
||||
>
|
||||
<icon-currency
|
||||
:currency="item.symbol.toLowerCase()"
|
||||
class="mr-1.5 w-6 h-6"
|
||||
/>
|
||||
|
||||
<span
|
||||
class="text-primary-blue-dark text-sm font-semibold"
|
||||
>
|
||||
{{ item.symbol }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</List>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex justify-between items-center text-primary-gray font-medium"
|
||||
>
|
||||
<div>~ {{ formatUsd(token0.amountUSD) }}</div>
|
||||
|
||||
<div>
|
||||
Balance: {{ formatDecimal(token0.balance) }} {{ token0.symbol }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute -bottom-1 border border-grey-dark/[0.15] border-b-0 inset-x-0 mx-auto rounded-t-full bg-white w-10 h-5 shadow-sm"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="shadow-sm relative border border-grey-dark/[0.15] rounded-lg p-4"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
v-model.trim="token1.amount"
|
||||
readonly
|
||||
inputmode="decimal"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
pattern="^[0-9]*[.,]?[0-9]*$"
|
||||
placeholder="0.0"
|
||||
minlength="1"
|
||||
maxlength="79"
|
||||
spellcheck="false"
|
||||
:size="
|
||||
String(token1.amount).length > 0
|
||||
? Math.min(String(token1.amount).length, 24)
|
||||
: 2
|
||||
"
|
||||
class="border-0 focus:outline-none focus:ring-0 text-lg font-semibold px-0 max-w-full"
|
||||
/>
|
||||
<span class="text-primary-gray text-lg font-semibold">{{
|
||||
token1.symbol
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<Dropdown>
|
||||
<template #trigger="{ toggle }">
|
||||
<button
|
||||
class="flex items-center hover:bg-primary-blue-dark/10 p-1.5 rounded"
|
||||
@click="toggle"
|
||||
>
|
||||
<svg
|
||||
width="9"
|
||||
height="6"
|
||||
viewBox="0 0 9 6"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4.5 4.5L3.89896 5.10104C4.2309 5.43299 4.76909 5.43299 5.10104 5.10104L4.5 4.5ZM8.60104 1.60104C8.93299 1.2691 8.93299 0.730905 8.60104 0.398959C8.2691 0.0670136 7.7309 0.0670135 7.39896 0.398959L8.60104 1.60104ZM1.60104 0.398959C1.26909 0.0670133 0.730905 0.0670132 0.398959 0.398959C0.0670138 0.730905 0.0670137 1.26909 0.398959 1.60104L1.60104 0.398959ZM5.10104 5.10104L8.60104 1.60104L7.39896 0.398959L3.89896 3.89896L5.10104 5.10104ZM5.10104 3.89896L1.60104 0.398959L0.398959 1.60104L3.89896 5.10104L5.10104 3.89896Z"
|
||||
fill="#3F75FF"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<icon-currency
|
||||
:currency="token1.symbol.toLowerCase()"
|
||||
class="ml-2.5 w-9 h-9"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
<template #menu="{ close }">
|
||||
<DropdownMenu
|
||||
class="z-10 bg-white w-32 rounded border border-[#CFDCFF] drop-shadow-sm pt-0 pb-0"
|
||||
align="right"
|
||||
>
|
||||
<List
|
||||
:items="allTokens.filter(t => t.symbol != token0.symbol)"
|
||||
class="p-1.5"
|
||||
items-wrapper-classes="space-y-2 overflow-y-scroll max-h-[200px]"
|
||||
>
|
||||
<template v-slot:default="{ item }">
|
||||
<div
|
||||
@click="
|
||||
selectToken1(item);
|
||||
close();
|
||||
"
|
||||
class="cursor-pointer rounded-sm flex items-center p-1.5 bg-[#F6F7F9] border border-transparent hover:border-primary-blue-dark"
|
||||
>
|
||||
<icon-currency
|
||||
:currency="item.symbol.toLowerCase()"
|
||||
class="mr-1.5 w-6 h-6"
|
||||
/>
|
||||
|
||||
<span
|
||||
class="text-primary-blue-dark text-sm font-semibold"
|
||||
>
|
||||
{{ item.symbol }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</List>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex justify-between items-center text-primary-gray font-medium"
|
||||
>
|
||||
<div>
|
||||
~ {{ formatUsd(token1.amountUSD) }}
|
||||
<span v-if="slippagePerc" class="text-green-pure"
|
||||
>({{ formatPercent(slippagePerc) }})</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Balance: {{ formatDecimal(token1.balance) }} {{ token1.symbol }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="shadow-sm absolute -top-1 border border-grey-dark/[0.15] border-t-0 inset-x-0 mx-auto rounded-b-full bg-white w-10 h-5"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="absolute inset-y-0 my-auto h-2 w-full bg-white"></div>
|
||||
<div
|
||||
@click="switchTokens"
|
||||
class="cursor-pointer absolute inset-0 m-auto h-[30px] w-[30px] bg-white border border-[#D1D7E1] rounded-full flex items-center justify-center drop-shadow-sm"
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.25333 6.0701C2.26024 6.14317 2.27865 6.21659 2.30753 6.2869C2.42378 6.5674 2.69665 6.74979 3.0004 6.74979H4.5004L4.5004 14.9998H6.0004L6.0004 6.74979H7.5004C7.8034 6.74979 8.07702 6.5674 8.19327 6.2869C8.23152 6.1939 8.2504 6.09654 8.2504 5.99979C8.2504 5.80479 8.17392 5.61277 8.03067 5.46952L5.78067 3.21952C5.48742 2.92627 5.01263 2.92627 4.72013 3.21952L2.47013 5.46952C2.30925 5.63039 2.23259 5.8509 2.25333 6.0701ZM9.75333 11.9295C9.73259 12.1487 9.80925 12.3692 9.97012 12.5301L12.2201 14.7801C12.5126 15.0733 12.9874 15.0733 13.2807 14.7801L15.5307 12.5301C15.6739 12.3868 15.7504 12.1948 15.7504 11.9998C15.7504 11.903 15.7315 11.8057 15.6933 11.7127C15.577 11.4322 15.3034 11.2498 15.0004 11.2498H13.5004L13.5004 2.99979H12.0004L12.0004 11.2498H10.5004C10.1974 11.2498 9.92378 11.4322 9.80753 11.7127C9.77865 11.783 9.76024 11.8564 9.75333 11.9295Z"
|
||||
fill="#3F75FF"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="text-right mt-[18px] font-medium mb-1"
|
||||
v-if="
|
||||
token0.amount &&
|
||||
token1.amount &&
|
||||
token1.amount != '0' &&
|
||||
token0.amount != '0'
|
||||
"
|
||||
>
|
||||
1 {{ token1.symbol }} =
|
||||
{{ formatDecimal(token0.amount / token1.amount, 7) }}
|
||||
{{ token0.symbol }}
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center justify-between">
|
||||
<div class="text-[#A5AEC5] text-sm font-medium">
|
||||
Slippage tolerance
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<toggle-button
|
||||
:checked="customSlippage"
|
||||
@change="customSlippage = $event"
|
||||
label="Custom"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex items-center justify-between space-x-4">
|
||||
<div class="w-1/2">
|
||||
<div
|
||||
v-if="customSlippage"
|
||||
class="text-sm font-medium flex text-center items-center bg-primary-gray/[.15] text-primary-gray w-full p-1 rounded-[6px] border border-grey-dark border-opacity-[0.15]"
|
||||
>
|
||||
<span class="w-1/4 py-1.5">0.1%</span>
|
||||
<span class="w-1/4 py-1.5">0.5%</span>
|
||||
<span class="w-1/4 py-1.5">1%</span>
|
||||
<span class="w-1/4 py-1.5">3%</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="text-sm font-medium flex items-center border-primary-blue-dark text-primary-gray w-full p-1 rounded-[6px] border "
|
||||
>
|
||||
<button
|
||||
class="w-1/4 py-1.5"
|
||||
@click="slippage = '0.1'"
|
||||
:class="{
|
||||
'rounded bg-primary-blue-dark text-white': slippage === '0.1'
|
||||
}"
|
||||
>
|
||||
0.1%
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="w-1/4 py-1.5"
|
||||
@click="slippage = '0.5'"
|
||||
:class="{
|
||||
'rounded bg-primary-blue-dark text-white': slippage === '0.5'
|
||||
}"
|
||||
>
|
||||
0.5%
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="w-1/4 py-1.5"
|
||||
@click="slippage = '1'"
|
||||
:class="{
|
||||
'rounded bg-primary-blue-dark text-white': slippage === '1'
|
||||
}"
|
||||
>
|
||||
1%
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="w-1/4 py-1.5"
|
||||
@click="slippage = '3'"
|
||||
:class="{
|
||||
'rounded bg-primary-blue-dark text-white': slippage === '3'
|
||||
}"
|
||||
>
|
||||
3%
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<input-percent
|
||||
ref="customPercentInput"
|
||||
right
|
||||
max="10"
|
||||
v-model="customSlippageValue"
|
||||
placeholder="Custom"
|
||||
:disabled="!customSlippage"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-7">
|
||||
<ButtonCTA
|
||||
@click="swap"
|
||||
:loading="swapping"
|
||||
:disabled="swapping"
|
||||
class="px-8 h-16 w-full"
|
||||
>Swap</ButtonCTA
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, defineComponent, reactive, ref, watch } from '@nuxtjs/composition-api'
|
||||
import { useNetwork } from '~/composables/useNetwork'
|
||||
import tokens from '~/constant/tokens'
|
||||
import Button from '../Button.vue'
|
||||
import Dropdown from '../common/input/Dropdown.vue'
|
||||
import InputPercent from '../common/input/InputPercent.vue'
|
||||
import ToggleButton from '../common/input/ToggleButton.vue'
|
||||
import List from '../common/list/List.vue'
|
||||
import Menu from '../common/menu/Menu.vue'
|
||||
import IconCurrency from '../IconCurrency.vue'
|
||||
import DropdownMenu from '../protocols/DropdownMenu.vue'
|
||||
import ButtonCTA from '~/components/common/input/ButtonCTA.vue'
|
||||
import { useFormatting } from '~/composables/useFormatting'
|
||||
import { useBalances } from '~/composables/useBalances'
|
||||
import { useBigNumber } from '~/composables/useBigNumber'
|
||||
|
||||
export default defineComponent({
|
||||
components: { List, Menu, IconCurrency, Button, Dropdown, DropdownMenu, ToggleButton, InputPercent, ButtonCTA },
|
||||
props: {
|
||||
onSwap: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
token1Amount: {
|
||||
type: String,
|
||||
default: '0'
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
|
||||
const { toBN } = useBigNumber()
|
||||
const { formatDecimal, formatUsd, formatPercent } = useFormatting()
|
||||
const { prices, getBalanceByKey } = useBalances()
|
||||
const { activeNetworkId } = useNetwork()
|
||||
const allTokens = computed(() => tokens[activeNetworkId.value].allTokens)
|
||||
|
||||
const token0Input = ref()
|
||||
|
||||
const token0 = reactive({
|
||||
address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
|
||||
symbol: 'ETH',
|
||||
amount: '0',
|
||||
amountUSD: computed(() => (prices[activeNetworkId.value][token0.address] * token0.amount) || '0'),
|
||||
balance: computed(() => getBalanceByKey(token0.symbol)),
|
||||
decimals: 18,
|
||||
|
||||
})
|
||||
|
||||
const token1 = reactive({
|
||||
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
|
||||
symbol: 'DAI',
|
||||
amount: props.token1Amount,
|
||||
amountUSD: computed(() => (prices[activeNetworkId.value][token1.address] * token1.amount) || '0'),
|
||||
balance: computed(() => getBalanceByKey(token1.symbol)),
|
||||
decimals: 18,
|
||||
})
|
||||
|
||||
const slippagePerc = computed(() => {
|
||||
|
||||
const sellAmountUsd = toBN(token0.amountUSD);
|
||||
const buyAmountUsd = toBN(token1.amountUSD);
|
||||
|
||||
return buyAmountUsd.isZero() || sellAmountUsd.isZero() ? 0 : Math.abs(buyAmountUsd.dividedBy(sellAmountUsd).minus(1).toString())
|
||||
})
|
||||
|
||||
watch(() => props.token1Amount, () => {
|
||||
token1.amount = props.token1Amount
|
||||
});
|
||||
|
||||
const slippage = ref('3')
|
||||
const customSlippage = ref(false)
|
||||
const customSlippageValue = ref("0.0")
|
||||
|
||||
const switchTokens = () => {
|
||||
const token0Raw = JSON.parse(JSON.stringify(token0))
|
||||
const token1Raw = JSON.parse(JSON.stringify(token1))
|
||||
|
||||
token0.symbol = token1Raw.symbol
|
||||
token0.address = token1Raw.address
|
||||
token0.amount = token1Raw.amount
|
||||
token0.decimals = token1Raw.decimals
|
||||
|
||||
token1.symbol = token0Raw.symbol
|
||||
token1.address = token0Raw.address
|
||||
token1.amount = token0Raw.amount
|
||||
token1.decimals = token0Raw.decimals
|
||||
}
|
||||
|
||||
const selectToken0 = (token) => {
|
||||
token0.symbol = token.symbol
|
||||
token0.address = token.address
|
||||
token0.decimals = token.decimals
|
||||
token0.amount = "0"
|
||||
}
|
||||
|
||||
const selectToken1 = (token) => {
|
||||
token1.symbol = token.symbol
|
||||
token1.address = token.address
|
||||
token1.decimals = token.decimals
|
||||
token1.amount = "0"
|
||||
}
|
||||
|
||||
watch(token0, () => emit('token0', { ...token0 }), { immediate: true })
|
||||
watch(token1, () => emit('token1', { ...token1 }), { immediate: true })
|
||||
watch([slippage, customSlippage, customSlippageValue], () => emit('slippage', customSlippage.value ? customSlippageValue.value : slippage.value), { immediate: true })
|
||||
watch(activeNetworkId, () => {
|
||||
selectToken0(allTokens.value.find(token => token.symbol === 'ETH'))
|
||||
selectToken1(allTokens.value.find(token => token.symbol === 'DAI'))
|
||||
}, { immediate: true })
|
||||
|
||||
const swapping = ref(false)
|
||||
|
||||
const swap = async () => {
|
||||
swapping.value = true
|
||||
|
||||
if (props.onSwap) {
|
||||
await props.onSwap(
|
||||
{ ...token0 },
|
||||
{ ...token1 },
|
||||
slippage.value
|
||||
)
|
||||
}
|
||||
|
||||
swapping.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
formatPercent,
|
||||
slippagePerc,
|
||||
formatDecimal,
|
||||
formatUsd,
|
||||
token0Input,
|
||||
customSlippage,
|
||||
token0,
|
||||
selectToken0,
|
||||
token1,
|
||||
selectToken1,
|
||||
switchTokens,
|
||||
allTokens,
|
||||
customSlippageValue,
|
||||
slippage,
|
||||
swap,
|
||||
swapping,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
60
composables/swap/use1InchSwap.ts
Normal file
60
composables/swap/use1InchSwap.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { useBigNumber } from "../useBigNumber";
|
||||
import { useDSA } from "../useDSA";
|
||||
import { useFormatting } from "../useFormatting";
|
||||
import { useMath } from "../useMath";
|
||||
import { Network, useNetwork } from "../useNetwork";
|
||||
|
||||
export const use1InchSwap = () => {
|
||||
const { divWithDec } = useMath();
|
||||
const { activeNetworkId } = useNetwork();
|
||||
const { dsa, activeAccount } = useDSA();
|
||||
const { isZero, ensureValue, gt, minus, times, plus, toBN } = useBigNumber();
|
||||
const { formatPercent, getFractionDigits } = useFormatting();
|
||||
|
||||
/**
|
||||
* @dev Get different sell spell, based on simulation mode on/offand account network .
|
||||
* Shared params:
|
||||
* @param {string} params.buyAddr - buying token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param {string} params.sellAddr - selling token amount.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param {number} params.sellAmt - selling token amount.
|
||||
* @param {number} params.unitAmt - unit amount of buyAmt/sellAmt with slippage.
|
||||
* @param {number} params.setId - set token amount at this ID in `InstaMemory` Contract.
|
||||
*
|
||||
* 1Inch related params:
|
||||
* @param {string} params.calldata - data from 1inch API.
|
||||
*
|
||||
* 1Split related params(using in simulation mode):
|
||||
* @param {number[]} params.distribution - distribution of swap across different dex.
|
||||
* @param {number} params.disableDexes - disable a dex. (To disable none: 0)
|
||||
* @param {number} params.getId - get token amount at this ID from `InstaMemory` Contract.
|
||||
*/
|
||||
function getSellSpell({
|
||||
buyAddr,
|
||||
sellAddr,
|
||||
sellAmt,
|
||||
unitAmt,
|
||||
calldata,
|
||||
setId
|
||||
}) {
|
||||
if (
|
||||
activeNetworkId.value === Network.Polygon ||
|
||||
activeAccount.value.version == 2
|
||||
) {
|
||||
return {
|
||||
connector: "1INCH-A",
|
||||
method: "sell",
|
||||
args: [buyAddr, sellAddr, sellAmt, unitAmt, calldata, setId]
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
connector: "oneInch",
|
||||
method: "sellThree",
|
||||
args: [buyAddr, sellAddr, sellAmt, unitAmt, calldata, setId]
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
getSellSpell
|
||||
};
|
||||
};
|
66
composables/useMath.ts
Normal file
66
composables/useMath.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { useBigNumber } from './useBigNumber'
|
||||
|
||||
const { pow, div, toBN, lt, isZero } = useBigNumber()
|
||||
|
||||
export function useMath() {
|
||||
// Convert bigNumber in string (use to save us from big number error on web3)
|
||||
// TODO - start using big number library for it?
|
||||
function bigNumInString(x) {
|
||||
if (Math.abs(x) < 1.0) {
|
||||
const e = parseInt(x.toString().split('e-')[1])
|
||||
if (e) {
|
||||
x *= Math.pow(10, e - 1)
|
||||
x = '0.' + new Array(e).join('0') + x.toString().substring(2)
|
||||
}
|
||||
} else {
|
||||
let e = parseInt(x.toString().split('+')[1])
|
||||
if (e > 20) {
|
||||
e -= 20
|
||||
x /= Math.pow(10, e)
|
||||
x += new Array(e + 1).join('0')
|
||||
}
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Use to convert large decimal into small. Eg:- number 321.242312 with power 3 = 321.242
|
||||
function cleanDecimal(num, power) {
|
||||
let MUL_DIV = 100
|
||||
if (power || power === 0) {
|
||||
MUL_DIV = 10 ** power
|
||||
} else {
|
||||
if (num < 0.01) MUL_DIV = 10 ** 6
|
||||
if (num < 1) MUL_DIV = 10 ** 4
|
||||
}
|
||||
return Math.floor(Number(num) * MUL_DIV) / MUL_DIV
|
||||
}
|
||||
|
||||
function roundDecimals(value) {
|
||||
if (isZero(value)) return 0.0
|
||||
if (lt(value, '0.001')) return cleanDecimal(toBN(value).toNumber(), 6)
|
||||
if (lt(value, '0.01')) return cleanDecimal(toBN(value).toNumber(), 5)
|
||||
if (lt(value, '0.1')) return cleanDecimal(toBN(value).toNumber(), 4)
|
||||
if (lt(value, '1')) return cleanDecimal(toBN(value).toNumber(), 3)
|
||||
return cleanDecimal(toBN(value).toNumber(), 3)
|
||||
}
|
||||
|
||||
function divWithDec(num, power) {
|
||||
power = typeof power !== 'undefined' ? power : 0
|
||||
const divider = pow('10', power)
|
||||
return div(num, divider).toFixed()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks divisor for 0. Returns 0 instead of NaN.
|
||||
*
|
||||
* @param {number} divident Divident.
|
||||
* @param {number} divisor Divisor.
|
||||
*/
|
||||
function safeDivide(divident, divisor) {
|
||||
if (!divisor) return 0
|
||||
|
||||
return divident / divisor
|
||||
}
|
||||
|
||||
return { bigNumInString, divWithDec, cleanDecimal, safeDivide, roundDecimals }
|
||||
}
|
21
ecosystem.config.js
Normal file
21
ecosystem.config.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'assembly',
|
||||
exec_mode: 'cluster',
|
||||
instances: 'max',
|
||||
script: './node_modules/nuxt/bin/nuxt.js',
|
||||
args: 'start',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
HOST: 'localhost',
|
||||
PORT: 4000,
|
||||
},
|
||||
env_production: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: '4000',
|
||||
HOST: 'localhost'
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
"v-tooltip": "^2.1.3",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-composable": "^1.0.0-beta.23",
|
||||
"waait": "^1.0.5",
|
||||
"walletlink": "^2.1.6",
|
||||
"web3": "^1.4.0",
|
||||
"web3modal": "^1.9.3"
|
||||
|
|
189
pages/1inch.vue
Normal file
189
pages/1inch.vue
Normal file
|
@ -0,0 +1,189 @@
|
|||
<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 class="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]"
|
||||
>
|
||||
<OneInchIcon Icon class="w-10 h-10 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="ml-4 text-primary-black text-2xl font-semibold">1Inch</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto w-[512px] shadow rounded-[10px] px-8 py-12">
|
||||
<swap-card
|
||||
:on-swap="swap"
|
||||
@token0="sellToken = $event"
|
||||
@token1="buyToken = $event"
|
||||
@slippage="slippage = $event"
|
||||
:token1Amount="buyToken ? buyToken.amount : '0'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
watch
|
||||
} from "@nuxtjs/composition-api";
|
||||
import BackIcon from "~/assets/icons/back.svg?inline";
|
||||
import OneInchIcon from "~/assets/icons/1inch.svg?inline";
|
||||
import SwapCard from "~/components/swap/SwapCard.vue";
|
||||
import wait from "waait";
|
||||
import axios from "axios";
|
||||
import { useToken } from "~/composables/useToken";
|
||||
import { useBigNumber } from "~/composables/useBigNumber";
|
||||
import { useNetwork } from "~/composables/useNetwork";
|
||||
import { useDSA } from "~/composables/useDSA";
|
||||
import { use1InchSwap } from "~/composables/swap/use1InchSwap";
|
||||
import { useWeb3 } from "~/composables/useWeb3";
|
||||
import { useNotification } from "~/composables/useNotification";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
BackIcon,
|
||||
OneInchIcon,
|
||||
SwapCard
|
||||
},
|
||||
setup() {
|
||||
const { toBN, pow, div } = useBigNumber();
|
||||
const { activeNetwork } = useNetwork();
|
||||
const { dsa } = useDSA();
|
||||
const { getSellSpell } = use1InchSwap();
|
||||
const { account } = useWeb3();
|
||||
const { showPendingTransaction, showWarning } = useNotification();
|
||||
const { valInt } = useToken();
|
||||
const sellToken = ref();
|
||||
const buyToken = ref();
|
||||
const slippage = ref("3.0");
|
||||
|
||||
function caculateUnitAmt() {
|
||||
let unitAmt: any = toBN(buyToken.value.amount).dividedBy(
|
||||
toBN(sellToken.value.amount)
|
||||
);
|
||||
unitAmt = unitAmt.multipliedBy((100 - 0.3) / 100);
|
||||
unitAmt = unitAmt.multipliedBy(1e18).toFixed(0);
|
||||
|
||||
return unitAmt;
|
||||
}
|
||||
|
||||
const swap = async () => {
|
||||
const result = await fetchSwapData();
|
||||
|
||||
const spells = dsa.value.Spell();
|
||||
|
||||
spells.add(
|
||||
getSellSpell({
|
||||
buyAddr: buyToken.value.address,
|
||||
sellAddr: sellToken.value.address,
|
||||
sellAmt: valInt(sellToken.value.amount, sellToken.value.decimals),
|
||||
unitAmt: caculateUnitAmt(),
|
||||
calldata: result.tx.data,
|
||||
setId: 0
|
||||
})
|
||||
);
|
||||
|
||||
try {
|
||||
const txHash = await dsa.value.cast({
|
||||
spells,
|
||||
from: account.value
|
||||
});
|
||||
|
||||
showPendingTransaction(txHash);
|
||||
} catch (error) {
|
||||
showWarning(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchSwapQuote = async () => {
|
||||
if (!sellToken.value || !buyToken.value) return;
|
||||
|
||||
if (!sellToken.value.amount || sellToken.value.amount === "0") {
|
||||
buyToken.value.amount = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = await axios.get(
|
||||
`https://api.1inch.exchange/v3.0/${activeNetwork.value.chainId}/quote`,
|
||||
{
|
||||
params: {
|
||||
fromTokenAddress: sellToken.value.address,
|
||||
toTokenAddress: buyToken.value.address,
|
||||
amount: valInt(sellToken.value.amount, sellToken.value.decimals)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const num = toBN(data.toTokenAmount);
|
||||
const multiplier = pow(10, buyToken.value.decimals);
|
||||
|
||||
buyToken.value.amount = div(num, multiplier).toFixed(7);
|
||||
};
|
||||
|
||||
const fetchSwapData = async () => {
|
||||
if (!sellToken.value || !buyToken.value) return;
|
||||
|
||||
if (!sellToken.value.amount || sellToken.value.amount === "0") {
|
||||
buyToken.value.amount = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = await axios.get(
|
||||
`https://api.1inch.exchange/v3.0/${activeNetwork.value.chainId}/swap`,
|
||||
{
|
||||
params: {
|
||||
fromTokenAddress: sellToken.value.address.toLowerCase(),
|
||||
toTokenAddress: buyToken.value.address.toLowerCase(),
|
||||
amount: valInt(sellToken.value.amount, sellToken.value.decimals),
|
||||
fromAddress: dsa.value.instance.address.toLowerCase(),
|
||||
slippage: slippage.value,
|
||||
disableEstimate: true
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
watch([sellToken, buyToken, slippage], fetchSwapQuote);
|
||||
let interval;
|
||||
|
||||
onMounted(() => {
|
||||
interval = setInterval(fetchSwapQuote, 10000);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
swap,
|
||||
slippage,
|
||||
sellToken,
|
||||
buyToken
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -35,6 +35,7 @@ import { useNetwork } from "~/composables/useNetwork";
|
|||
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";
|
||||
|
||||
const appsPerNetwork = {
|
||||
mainnet: [
|
||||
|
@ -58,6 +59,13 @@ const appsPerNetwork = {
|
|||
name: "MakerDAO",
|
||||
url: "/mainnet/maker",
|
||||
description: "Collateralized Debt"
|
||||
},
|
||||
{
|
||||
id: "1inch",
|
||||
icon: OneInchIcon,
|
||||
name: "1inch",
|
||||
url: "/1inch",
|
||||
description: "DEX Aggregator"
|
||||
}
|
||||
],
|
||||
polygon: [
|
||||
|
@ -67,6 +75,13 @@ const appsPerNetwork = {
|
|||
name: "Aave v2",
|
||||
url: "/polygon/aave-v2",
|
||||
description: "Lend and borrow straight from your Gnosis Safe"
|
||||
},
|
||||
{
|
||||
id: "1inch",
|
||||
icon: OneInchIcon,
|
||||
name: "1inch",
|
||||
url: "/1inch",
|
||||
description: "DEX Aggregator"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
@ -11642,6 +11642,11 @@ vuex@^3.6.2:
|
|||
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71"
|
||||
integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==
|
||||
|
||||
waait@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/waait/-/waait-1.0.5.tgz#6a3c7aaa88bd0a1a545e9d47890b9595bebf9aa7"
|
||||
integrity sha512-wp+unA4CpqxvBUKHHv8D86fK4jWByHAWyhEXXVHfVUZfK+16ylpj7hjQ58Z8j9ntu8XNukRQT8Fi5qbyJ8rkyw==
|
||||
|
||||
walletlink@^2.1.6:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/walletlink/-/walletlink-2.1.6.tgz#4e48310af09bb0c940a156c26c1d0b1b9506ddb9"
|
||||
|
|
Loading…
Reference in New Issue
Block a user