diff --git a/components/swap/SwapCard.vue b/components/swap/SwapCard.vue index a00aca2..111b4bc 100644 --- a/components/swap/SwapCard.vue +++ b/components/swap/SwapCard.vue @@ -362,6 +362,7 @@ import ButtonCTA from '~/components/common/input/ButtonCTA.vue' import { useFormatting } from '~/composables/useFormatting' import { useBalances } from '~/composables/useBalances' import { useBigNumber } from '~/composables/useBigNumber' +import { use1InchSwap } from '~/composables/swap/use1InchSwap' export default defineComponent({ components: { List, Menu, IconCurrency, Button, Dropdown, DropdownMenu, ToggleButton, InputPercent, ButtonCTA }, diff --git a/composables/swap/use1InchSwap.ts b/composables/swap/use1InchSwap.ts index 6fc8d21..43a9846 100644 --- a/composables/swap/use1InchSwap.ts +++ b/composables/swap/use1InchSwap.ts @@ -1,7 +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 swap = ({ buyAddress, sellAddress, sellAmount, unitAmount }) => {}; + 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 { - swap + getSellSpell }; }; diff --git a/composables/useMath.ts b/composables/useMath.ts new file mode 100644 index 0000000..09a9c08 --- /dev/null +++ b/composables/useMath.ts @@ -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 } +} diff --git a/pages/1inch.vue b/pages/1inch.vue index 9b12260..e4a09ca 100644 --- a/pages/1inch.vue +++ b/pages/1inch.vue @@ -31,7 +31,7 @@ :on-swap="swap" @token0="sellToken = $event" @token1="buyToken = $event" - @slippage="fee = $event" + @slippage="slippage = $event" :token1Amount="buyToken ? buyToken.amount : '0'" /> @@ -54,6 +54,10 @@ 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: { @@ -64,17 +68,65 @@ export default defineComponent({ 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 fee = ref("3.0"); + const slippage = ref("3.0"); - const swap = async (token0, token1, slippage) => { - await wait(3000); + 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(); + + console.log( + getSellSpell({ + buyAddr: buyToken.value.address, + sellAddr: sellToken.value.address, + sellAmt: sellToken.value.balance, + unitAmt: caculateUnitAmt(), + calldata: result.tx.data, + setId: 0 + }) + ); + + 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 fetchSwapInfo = async () => { + const fetchSwapQuote = async () => { if (!sellToken.value || !buyToken.value) return; if (!sellToken.value.amount || sellToken.value.amount === "0") { @@ -88,8 +140,7 @@ export default defineComponent({ params: { fromTokenAddress: sellToken.value.address, toTokenAddress: buyToken.value.address, - amount: valInt(sellToken.value.amount, sellToken.value.decimals), - fee: fee.value + amount: valInt(sellToken.value.amount, sellToken.value.decimals) } } ); @@ -100,11 +151,36 @@ export default defineComponent({ buyToken.value.amount = div(num, multiplier).toFixed(7); }; - watch([sellToken, buyToken, fee], fetchSwapInfo); + 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(fetchSwapInfo, 10000); + interval = setInterval(fetchSwapQuote, 10000); }); onBeforeUnmount(() => { @@ -115,7 +191,7 @@ export default defineComponent({ return { swap, - fee, + slippage, sellToken, buyToken };