From 08a95d67e4aac4da979456b5bacef00635a6cbd2 Mon Sep 17 00:00:00 2001 From: Keno Dressel Date: Mon, 29 Jul 2024 11:17:08 +0200 Subject: [PATCH 01/11] fix: allows wrong network modal to be closed --- src/store/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/store/index.js b/src/store/index.js index 6e6503f0..87dd516d 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -305,7 +305,6 @@ export default createStore({ await dispatch('modals/open', { name: 'show-error', message: `Network ${newNetworkId} is not supported, please switch to Testnet`, - resolve: null, }); } else { if (networkId !== newNetworkId) { From 1d5beda524fb6747ad7fdec7759725b3deabe106 Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Wed, 14 Aug 2024 11:12:25 +0200 Subject: [PATCH 02/11] feat: use backend data for price and volume in TokenDetailView --- src/components/explore/PairTable.vue | 6 +- src/components/explore/TokenTable.vue | 6 +- src/store/modules/dexBackend.js | 6 +- src/views/TokenDetailView.vue | 125 ++++++++++++-------------- 4 files changed, 66 insertions(+), 77 deletions(-) diff --git a/src/components/explore/PairTable.vue b/src/components/explore/PairTable.vue index 102821d1..39760831 100644 --- a/src/components/explore/PairTable.vue +++ b/src/components/explore/PairTable.vue @@ -58,15 +58,15 @@ export default { value: pair.tvlUsd, }, volumeDay: { - text: formatUsdPretty(pair.volumeUsdDay, 0), + text: formatUsdPretty(pair.volumeUsdDay || 0, 0), value: pair.volumeUsdDay, }, volumeMonth: { - text: formatUsdPretty(pair.volumeUsdMonth, 0), + text: formatUsdPretty(pair.volumeUsdMonth || 0, 0), value: pair.volumeUsdMonth, }, volumeAll: { - text: formatUsdPretty(pair.volumeUsdAll, 0), + text: formatUsdPretty(pair.volumeUsdAll || 0, 0), value: pair.volumeUsdAll, }, })); diff --git a/src/components/explore/TokenTable.vue b/src/components/explore/TokenTable.vue index e04b193e..85a2319d 100644 --- a/src/components/explore/TokenTable.vue +++ b/src/components/explore/TokenTable.vue @@ -74,15 +74,15 @@ export default { value: token.priceChangeMonth, }, volumeDay: { - text: formatUsdPretty(token.volumeUsdDay, 0), + text: formatUsdPretty(token.volumeUsdDay || 0, 0), value: token.volumeUsdDay, }, volumeMonth: { - text: formatUsdPretty(token.volumeUsdMonth, 0), + text: formatUsdPretty(token.volumeUsdMonth || 0, 0), value: token.volumeUsdMonth, }, volumeAll: { - text: formatUsdPretty(token.volumeUsdAll, 0), + text: formatUsdPretty(token.volumeUsdAll || 0, 0), value: token.volumeUsdAll, }, })); diff --git a/src/store/modules/dexBackend.js b/src/store/modules/dexBackend.js index 0c590b96..d58dfd67 100644 --- a/src/store/modules/dexBackend.js +++ b/src/store/modules/dexBackend.js @@ -156,6 +156,10 @@ export default { })); }, + async getTokenWithUsd({ dispatch }, tokenId) { + return dispatch('safeFetch', { url: `tokens/${tokenId}` }); + }, + async getAllTokens({ dispatch }) { return dispatch('safeFetch', { url: 'tokens' }); }, @@ -194,7 +198,7 @@ export default { }, async fetchPairsByToken({ dispatch }, tokenId) { - return dispatch('safeFetch', { url: `tokens/by-address/${tokenId}/pairs` }); + return dispatch('safeFetch', { url: `tokens/${tokenId}/pairs` }); }, async fetchPairsByTokenUsd({ dispatch }, tokenId) { diff --git a/src/views/TokenDetailView.vue b/src/views/TokenDetailView.vue index 16258bd3..f20aff91 100644 --- a/src/views/TokenDetailView.vue +++ b/src/views/TokenDetailView.vue @@ -2,7 +2,7 @@
-

{{ metaInfo?.symbol }} / {{ metaInfo?.name }}

+

{{ tokenWithUsd?.symbol }} / {{ tokenWithUsd?.name }}

@@ -37,12 +37,12 @@
- +
- + - +
@@ -54,7 +54,7 @@

Transactions

@@ -68,8 +68,8 @@

Token Information

- - + + d || 0); acc.datasets[2].data = [ ...acc.datasets[2].data, - new BigNumber(reserve).div(new BigNumber(10).pow(this.metaInfo.decimals)), + new BigNumber(reserve).div(new BigNumber(10).pow(this.tokenWithUsd.decimals)), ].map((d) => d || 0); if (tx.type === 'SwapTokens') { acc.datasets[3].data = [ @@ -160,7 +174,7 @@ export default defineComponent({ new BigNumber(this.getDeltaReserve(tx)) .abs() .multipliedBy(this.getUsdPrice(tx)) - .div(new BigNumber(10).pow(this.metaInfo.decimals)), + .div(new BigNumber(10).pow(this.tokenWithUsd.decimals)), ].map((d) => d || 0); } else { acc.datasets[3].data = [...acc.datasets[3].data, 0]; @@ -202,59 +216,25 @@ export default defineComponent({ })); }, supply() { - return formatAmountPretty(this.metaInfo.event_supply, this.metaInfo.decimals); - }, - priceRaw() { - // latest 20 history events - const latestHistoryEntries = this.history.slice(-5); - // average the price of the last events - const historicPriceEntries = latestHistoryEntries - .map((historyElement) => this.getUsdPrice(historyElement)) - .filter((price) => !price.isNaN()); - - return historicPriceEntries - .reduce((a, b) => a.plus(b), BigNumber(0)) - .div(historicPriceEntries.length); + return formatAmountPretty(this.totalSupply, this.tokenWithUsd.decimals); }, price() { - return formatUsdPretty(this.priceRaw, 0); - }, - lockedRaw() { - return this.pairs?.pairs0 - .map((pair) => pair.liquidityInfo.reserve0) - .filter((reserve) => !!reserve) - .reduce((a, b) => a.plus(b), BigNumber(0)) - .plus( - this.pairs.pairs1 - .map((pair) => pair.liquidityInfo.reserve1) - .filter((reserve) => !!reserve) - .reduce((a, b) => a.plus(b), BigNumber(0)), - ) - .div(new BigNumber(10).pow(this.metaInfo.decimals)); + return formatUsdPretty(this.tokenWithUsd.priceUsd, 0); }, locked() { - return formatAmountPretty(this.lockedRaw, 0); + return formatAmountPretty(this.tokenWithUsd.totalReserve, 0); }, tvl() { - return formatUsdPretty(new BigNumber(this.lockedRaw).multipliedBy(this.priceRaw), 0); + return formatUsdPretty(this.tokenWithUsd.tvlUsd, 0); }, fdv() { return formatUsdPretty( - new BigNumber(this.metaInfo.event_supply).multipliedBy(this.priceRaw).toString(), - this.metaInfo.decimals, + new BigNumber(this.totalSupply).multipliedBy(this.tokenWithUsd.priceUsd).toString(), + this.tokenWithUsd.decimals, ); }, - volume() { - return formatUsdPretty( - this.history - .filter((tx) => Date.now() - tx.microBlockTime < 24 * 60 * 60 * 1000) - .filter((tx) => tx.type === 'SwapTokens') - .reduce((acc, tx) => acc.plus(this.getUsdPrice(tx)), new BigNumber(0)), - 0, - ); - }, - fees() { - return 0; + volume24h() { + return formatUsdPretty(this.tokenWithUsd.volumeUsdDay || 0, 0); }, }, async mounted() { @@ -262,14 +242,19 @@ export default defineComponent({ // extract param from URL this.tokenId = this.$route.params.id; - const metaInfo = await this.$store.dispatch('tokens/fetchToken', this.tokenId); - this.metaInfo = detectAndModifyWAE({ - ...metaInfo, - address: this.tokenId, - }); + this.totalSupply = await this.$store + .dispatch('tokens/fetchToken', this.tokenId) + .then((r) => r.event_supply); this.pairs = await this.$store.dispatch('backend/fetchPairsByToken', this.tokenId); + this.tokenWithUsd = await this.$store.dispatch('backend/getTokenWithUsd', this.tokenId); + + this.tokenWithUsd = detectAndModifyWAE({ + ...this.tokenWithUsd, + address: this.tokenId, + }); + if (!this.pairs) { this.loading = false; // redirect to 404 @@ -282,7 +267,7 @@ export default defineComponent({ pair.address, { ...pair, - token0: this.metaInfo, + token0: this.tokenWithUsd, token1: detectAndModifyWAE(pair.oppositeToken), }, ]), @@ -291,7 +276,7 @@ export default defineComponent({ { ...pair, token0: detectAndModifyWAE(pair.oppositeToken), - token1: this.metaInfo, + token1: this.tokenWithUsd, }, ]), ]); From 3a18ba68b0ef723f5f87b996a1cbc4ce5b3e864c Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Wed, 21 Aug 2024 13:32:47 +0200 Subject: [PATCH 03/11] refactor: adapt improved backend api structure --- src/store/modules/dexBackend.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/modules/dexBackend.js b/src/store/modules/dexBackend.js index d58dfd67..b2af8357 100644 --- a/src/store/modules/dexBackend.js +++ b/src/store/modules/dexBackend.js @@ -128,7 +128,7 @@ export default { if (!pair) return null; } const resp = await dispatch('safeFetch', { - url: `pairs/by-address/${pairAddress || pair.address}`, + url: `pairs/${pairAddress || pair.address}`, }); return ( resp && { @@ -144,7 +144,7 @@ export default { }, async fetchSwapRoutes({ dispatch }, { tokenA, tokenB }) { - return dispatch('safeFetch', { url: `pairs/swap-routes/${tokenA}/${tokenB}` }); + return dispatch('safeFetch', { url: `swap-routes/${tokenA}/${tokenB}` }); }, async getListedTokens({ dispatch }) { From 1d616ee4c23e6a938a756c42f5f0edf48bd20480 Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Tue, 27 Aug 2024 17:09:23 +0200 Subject: [PATCH 04/11] feat: adapt new graph endpoint for ExploreView --- src/components/explore/PriceHistoryGraph.vue | 170 +++---------------- src/store/modules/dexBackend.js | 5 + src/views/ExploreView.vue | 10 +- 3 files changed, 35 insertions(+), 150 deletions(-) diff --git a/src/components/explore/PriceHistoryGraph.vue b/src/components/explore/PriceHistoryGraph.vue index d90f8212..328ab8de 100644 --- a/src/components/explore/PriceHistoryGraph.vue +++ b/src/components/explore/PriceHistoryGraph.vue @@ -102,11 +102,11 @@ export default { Bar, }, props: { + availableGraphTypes: { type: Array, required: true }, datasets: { type: Array, required: true }, x: { type: Array, required: true }, initialChart: { type: String, default: 'Volume' }, initialTimeFrame: { type: String, default: 'MAX' }, - loading: { type: Boolean, default: false }, }, data() { return { @@ -114,6 +114,11 @@ export default { selectedChart: null, colors: ['red', 'green', 'blue', 'purple', 'orange'], timeFrames: TIME_FRAMES, + graph: { + labels: [], + datasets: [], + }, + loading: false, }; }, computed: { @@ -137,7 +142,7 @@ export default { offset: true, min: Math.max( Date.now() - 1000 * 60 * 60 * this.timeFrames[this.selectedTimeFrame], - this.filteredData.filteredTime[0], + this.graph.labels[0], ), max: Date.now(), }, @@ -152,155 +157,26 @@ export default { }; }, labels() { - return this.datasets.map((d) => d.label); - }, - filteredData() { - const selectedDataSet = this.datasets.find((d) => d.label === this.selectedChart); - const minTime = - this.selectedTimeFrame === 'MAX' - ? Math.min(...this.x) - : Date.now() - 1000 * 60 * 60 * this.timeFrames[this.selectedTimeFrame]; - - const data = { - filteredData: selectedDataSet.data - .filter((_, i) => this.x[i] >= minTime) - .filter((d) => !new BigNumber(d).isNaN()), - excludedData: selectedDataSet.data - .filter((_, i) => this.x[i] < minTime) - .filter((d) => !new BigNumber(d).isNaN()), - filteredTime: this.x - .filter((_, i) => !new BigNumber(selectedDataSet.data[i]).isNaN()) - .filter((d) => d >= minTime) - .map((d) => Number(d)), - excludedTime: this.x - .filter((_, i) => !new BigNumber(selectedDataSet.data[i]).isNaN()) - .filter((d) => d < minTime) - .map((d) => Number(d)), - }; - - // interpolate data to show full frame - if ( - (['TVL', 'Locked'].includes(this.selectedChart) || this.selectedChart.includes('Price')) && - data.excludedData.length > 0 - ) { - // all of these are aggregated and summed, so we need to have baseline - data.filteredData.unshift(data.excludedData.pop()); - data.filteredTime.unshift(minTime); - data.excludedTime.pop(); - } - - if (['Volume', 'Fees'].includes(this.selectedChart) && data.filteredData.length > 0) { - // these just show the last value, so we need to have a baseline for the graph time but no value - // if there is no data, we do not need to add anything as we can show "no data" - data.filteredData.unshift(0); - data.filteredTime.unshift(minTime); - } - - if (this.selectedChart.includes('Price')) { - // as these always have current value, we need to add it to the end - // theoretically this is also required for TVL and locked, but we interpolate those in the graph based - // on the last value, so we don't need to add it here - data.filteredData.push(data.filteredData[data.filteredData.length - 1]); - data.filteredTime.push(Date.now()); - } - - return { - ...data, - selectedDataSet, - minTime, - }; + return this.availableGraphTypes; }, graphData() { - // filter data based on selected time - const { filteredTime, filteredData, selectedDataSet, minTime } = this.filteredData; - if ( - (filteredData.length === 0 || filteredTime.length === 0 || !selectedDataSet) && - (this.selectedChart === 'Fees' || this.selectedChart === 'Volume') - ) { - return { - labels: [], - datasets: [], - }; - } - - // aggregate data based on selected time - // retrieve min time from data or default to selected view - if (['TVL', 'Volume', 'Fees', 'Locked'].includes(this.selectedChart)) { - // these three charts are bar charts, so we need to calculate buckets - const bucketSize = (Date.now() - minTime) / 30; - - // seed empty buckets - const emptyBuckets = Object.fromEntries( - Array.from({ length: 31 }).map((_, i) => { - const key = minTime + i * bucketSize; - return [key, []]; - }), - ); - - const aggregatedData = filteredData.reduce((acc, d, i) => { - const time = filteredTime[i]; - const bucketIndex = Math.floor((time - minTime) / bucketSize); - const key = minTime + bucketIndex * bucketSize; - acc[key].push(d); - return acc; - }, emptyBuckets); - let bucketedData; - // interpolate TVL - if (['TVL', 'Locked'].includes(this.selectedChart)) { - // average TVL - let prevArr = []; - bucketedData = Object.fromEntries( - Object.entries(aggregatedData).map(([time, bucketArr], index) => { - let aggregatedValue = bucketArr - .reduce((acc, v) => acc.plus(v), new BigNumber(0)) - .div(bucketArr.length); - // interpolate TVL by filling in missing data with latest value from previous bucket - if (index > 0 && aggregatedValue.isNaN()) { - aggregatedValue = prevArr[prevArr.length - 1]; - } else { - prevArr = [...bucketArr]; - } - return [time, aggregatedValue]; - }), - ); - } else { - // sum fees and volume - bucketedData = Object.fromEntries( - Object.entries(aggregatedData).map(([time, bucketArr]) => [ - time, - bucketArr.reduce((acc, v) => acc.plus(v), new BigNumber(0)), - ]), - ); - } - return { - labels: Object.keys(bucketedData).map((x) => Number(x)), - datasets: [ - { - label: selectedDataSet.label, - data: Object.values(bucketedData).map((y) => Number(y)), - borderColor: 'rgb(0 255 157 / 80%)', - backgroundColor: 'rgb(0 255 157 / 80%)', - }, - ], - }; - } - return { - labels: filteredTime.map((x) => Number(x)), - datasets: [ - { - label: selectedDataSet.label, - data: filteredData.map((y) => Number(y)), - borderColor: 'rgb(0 255 157 / 80%)', - backgroundColor: 'rgb(0 255 157 / 80%)', - }, - ], + labels: this.graph.labels.map((l) => Number(l)), + datasets: this.graph.datasets.map((d) => ({ + label: d.label, + data: d.data.map((d) => Number(d)), + borderColor: 'rgb(0 255 157 / 80%)', + backgroundColor: 'rgb(0 255 157 / 80%)', + })), }; }, showBar() { return ['TVL', 'Volume', 'Fees', 'Locked'].includes(this.selectedChart); }, }, + async mounted() { + this.fetchData(); + }, created() { this.selectedTimeFrame = this.initialTimeFrame; this.selectedChart = this.initialChart; @@ -308,10 +184,20 @@ export default { methods: { changeTimeFrame(newTimeFrame) { this.selectedTimeFrame = newTimeFrame; + this.fetchData(); }, changeChartContent(newChart) { this.selectedChart = newChart; }, + async fetchData() { + this.loading = true; + this.graph.datasets = []; + this.graph = await this.$store.dispatch('backend/fetchGraph', { + graphType: this.selectedChart, + timeFrame: this.selectedTimeFrame, + }); + this.loading = false; + }, }, }; diff --git a/src/store/modules/dexBackend.js b/src/store/modules/dexBackend.js index b2af8357..cc4290e8 100644 --- a/src/store/modules/dexBackend.js +++ b/src/store/modules/dexBackend.js @@ -204,5 +204,10 @@ export default { async fetchPairsByTokenUsd({ dispatch }, tokenId) { return dispatch('safeFetch', { url: `pairs?token=${tokenId}` }); }, + + async fetchGraph({ dispatch }, options) { + const queryString = new URLSearchParams(options).toString(); + return dispatch('safeFetch', { url: `graph?${queryString}` }); + }, }, }; diff --git a/src/views/ExploreView.vue b/src/views/ExploreView.vue index fa30d11d..e9d82b61 100644 --- a/src/views/ExploreView.vue +++ b/src/views/ExploreView.vue @@ -6,9 +6,9 @@
@@ -16,9 +16,9 @@
@@ -97,7 +97,6 @@ export default defineComponent({ history: [], tokenMap: new Map(), activeTab: 'Tokens', - loading: false, }; }, computed: { @@ -165,17 +164,12 @@ export default defineComponent({ }, }, async mounted() { - this.loading = true; // fetch all tokens const tokens = await this.$store.dispatch('backend/getAllTokens'); this.tokenMap = new Map(tokens.map((token) => [token.address, detectAndModifyWAE(token)])); // fetch all pairs const fetchResult = await this.$store.dispatch('backend/fetchPairs'); this.pairs = Object.values(fetchResult); - - // fetch all history - this.history = await this.$store.dispatch('backend/fetchHistory'); - this.loading = false; }, methods: { pairToToken(pairAddress) { From 17de030167fcb552d0c1349fea1be4ada65e2c31 Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Wed, 28 Aug 2024 16:23:43 +0200 Subject: [PATCH 05/11] feat: adapt new graph endpoint for PoolDetailView --- src/components/explore/PriceHistoryGraph.vue | 36 ++++---- src/views/ExploreView.vue | 59 +------------ src/views/PoolDetailView.vue | 92 ++++---------------- 3 files changed, 44 insertions(+), 143 deletions(-) diff --git a/src/components/explore/PriceHistoryGraph.vue b/src/components/explore/PriceHistoryGraph.vue index 328ab8de..aef1afe9 100644 --- a/src/components/explore/PriceHistoryGraph.vue +++ b/src/components/explore/PriceHistoryGraph.vue @@ -4,10 +4,10 @@ v-for="label in labels" :key="label" class="p-16 hidden md:block" - :fill="label === selectedChart ? 'light' : 'transparent'" + :fill="label.type === selectedChart.type ? 'light' : 'transparent'" @click="changeChartContent(label)" > - {{ label }} + {{ label.text }}
@@ -18,7 +18,7 @@ @change="changeChartContent($event.target.value)" >
@@ -71,7 +71,6 @@ import { import { Line, Bar } from 'vue-chartjs'; import 'chartjs-adapter-date-fns'; import ButtonDefault from '@/components/ButtonDefault.vue'; -import BigNumber from 'bignumber.js'; const TIME_FRAMES = { '1H': 1, @@ -103,15 +102,17 @@ export default { }, props: { availableGraphTypes: { type: Array, required: true }, - datasets: { type: Array, required: true }, - x: { type: Array, required: true }, - initialChart: { type: String, default: 'Volume' }, + initialChart: { type: Object, required: true }, initialTimeFrame: { type: String, default: 'MAX' }, + pairId: { type: String, default: null }, }, data() { return { selectedTimeFrame: null, - selectedChart: null, + selectedChart: { + type: null, + text: null, + }, colors: ['red', 'green', 'blue', 'purple', 'orange'], timeFrames: TIME_FRAMES, graph: { @@ -150,7 +151,7 @@ export default { ticks: { // Include a dollar sign in the ticks callback: (value) => - ['TVL', 'Fees', 'Volume'].includes(this.selectedChart) ? `$${value}` : value, + ['TVL', 'Fees', 'Volume'].includes(this.selectedChart.type) ? `$${value}` : value, }, }, }, @@ -164,18 +165,18 @@ export default { labels: this.graph.labels.map((l) => Number(l)), datasets: this.graph.datasets.map((d) => ({ label: d.label, - data: d.data.map((d) => Number(d)), + data: d.data.map((n) => Number(n)), borderColor: 'rgb(0 255 157 / 80%)', backgroundColor: 'rgb(0 255 157 / 80%)', })), }; }, showBar() { - return ['TVL', 'Volume', 'Fees', 'Locked'].includes(this.selectedChart); + return ['TVL', 'Volume', 'Fees', 'Locked'].includes(this.selectedChart.type); }, }, async mounted() { - this.fetchData(); + await this.fetchData(); }, created() { this.selectedTimeFrame = this.initialTimeFrame; @@ -188,14 +189,19 @@ export default { }, changeChartContent(newChart) { this.selectedChart = newChart; + this.fetchData(); }, async fetchData() { this.loading = true; this.graph.datasets = []; - this.graph = await this.$store.dispatch('backend/fetchGraph', { - graphType: this.selectedChart, + let options = { + graphType: this.selectedChart.type, timeFrame: this.selectedTimeFrame, - }); + }; + if (this.pairId) { + options = { ...options, pairAddress: this.pairId }; + } + this.graph = await this.$store.dispatch('backend/fetchGraph', options); this.loading = false; }, }, diff --git a/src/views/ExploreView.vue b/src/views/ExploreView.vue index e9d82b61..8d7aec7e 100644 --- a/src/views/ExploreView.vue +++ b/src/views/ExploreView.vue @@ -4,21 +4,15 @@

TVL

Volume

@@ -74,7 +68,6 @@ import { defineComponent } from 'vue'; import ExploreWrapper from '@/components/explore/ExploreWrapper.vue'; import { mapGetters } from 'vuex'; -import BigNumber from 'bignumber.js'; import PriceHistoryGraph from '@/components/explore/PriceHistoryGraph.vue'; import PairTable from '@/components/explore/PairTable.vue'; import TransactionTable from '@/components/explore/TransactionTable.vue'; @@ -101,12 +94,6 @@ export default defineComponent({ }, computed: { ...mapGetters(['activeNetwork']), - tvl() { - return [this.graphData.datasets[0]]; - }, - volume() { - return [this.graphData.datasets[1]]; - }, pairTable() { return this.pairs.map((pair) => ({ ...pair, @@ -124,44 +111,6 @@ export default defineComponent({ tokenTable() { return [...this.tokenMap.values()]; }, - graphData() { - let tvl = new BigNumber(0); - return this.history.reduce( - (acc, tx) => { - // TVL - // deltaUsdValue is already calculated but absolute, so we need to check the deltaReserve to get the sign - const delta0 = new BigNumber(tx.delta0UsdValue).times(Math.sign(tx.deltaReserve0)); - const delta1 = new BigNumber(tx.delta1UsdValue).times(Math.sign(tx.deltaReserve1)); - tvl = tvl.plus(delta0.isNaN() ? 0 : delta0).plus(delta1.isNaN() ? 0 : delta1); - acc.datasets[0].data = [...acc.datasets[0].data, tvl.toString()].map((d) => d || 0); - - // VOLUME - if (tx.type === 'SwapTokens') { - acc.datasets[1].data = [ - ...acc.datasets[1].data, - new BigNumber(tx.delta0UsdValue).plus(tx.delta1UsdValue).toString(), - ].map((d) => d || 0); - } else { - acc.datasets[1].data = [...acc.datasets[1].data, 0].map((d) => d || 0); - } - acc.x = [...acc.x, tx.microBlockTime]; - return acc; - }, - { - x: [], - datasets: [ - { - label: 'TVL', - data: [], - }, - { - label: 'Volume', - data: [], - }, - ], - }, - ); - }, }, async mounted() { // fetch all tokens diff --git a/src/views/PoolDetailView.vue b/src/views/PoolDetailView.vue index b254eb22..5fd73b4f 100644 --- a/src/views/PoolDetailView.vue +++ b/src/views/PoolDetailView.vue @@ -3,18 +3,29 @@

- {{ - pair?.token0.symbol - }} + {{ pair?.token0.symbol }} + / - {{ - pair?.token1.symbol - }} + {{ pair?.token1.symbol }} +

- +
@@ -31,7 +42,7 @@ }) " > - {{ $t('poolDetail.swap') }} + {{ $t('poolDetail.swap') }}x { - // Price 0/1 - acc.datasets[0].data = [ - ...acc.datasets[0].data, - new BigNumber(tx.reserve0) - .div(BigNumber(10).pow(this.pair.token0.decimals)) - .div(new BigNumber(tx.reserve1).div(BigNumber(10).pow(this.pair.token1.decimals))) - .toString(), - ].map((d) => d || 0); - // Price 1/0 - acc.datasets[1].data = [ - ...(acc.datasets[1].data || []), - new BigNumber(tx.reserve1) - .div(BigNumber(10).pow(this.pair.token1.decimals)) - .div(new BigNumber(tx.reserve0).div(BigNumber(10).pow(this.pair.token0.decimals))) - .toString(), - ].map((d) => d || 0); - // TVL - acc.datasets[2].data = [ - ...acc.datasets[2].data, - new BigNumber(tx.reserve0Usd).plus(tx.reserve1Usd).toString(), - ].map((d) => d || 0); - // Fee - acc.datasets[3].data = [...acc.datasets[3].data, tx.txUsdFee].map((d) => d || 0); - // Volume - if (tx.type === 'SwapTokens') { - acc.datasets[4].data = [ - ...acc.datasets[4].data, - new BigNumber(tx.delta0UsdValue).plus(tx.delta1UsdValue).toString(), - ].map((d) => d || 0); - } else { - acc.datasets[4].data = [...acc.datasets[4].data, 0].map((d) => d || 0); - } - acc.x = [...acc.x, tx.microBlockTime]; - return acc; - }, - { - x: [], - datasets: [ - { - label: `${this.pair?.token1.symbol} / ${this.pair?.token0.symbol} Price`, - data: [], - }, - { - label: `${this.pair?.token0.symbol} / ${this.pair?.token1.symbol} Price`, - data: [], - }, - { - label: 'TVL', - data: [], - }, - { - label: 'Fees', - data: [], - }, - { - label: 'Volume', - data: [], - }, - ], - }, - ); - }, }, async mounted() { this.loading = true; From 076130daecd08dc09d10b3569946ed8241fb4d8e Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Thu, 29 Aug 2024 10:30:13 +0200 Subject: [PATCH 06/11] feat: adapt new graph endpoint for TokenDetailView --- src/components/explore/PriceHistoryGraph.vue | 4 + src/views/TokenDetailView.vue | 85 +++----------------- 2 files changed, 15 insertions(+), 74 deletions(-) diff --git a/src/components/explore/PriceHistoryGraph.vue b/src/components/explore/PriceHistoryGraph.vue index aef1afe9..650aaec3 100644 --- a/src/components/explore/PriceHistoryGraph.vue +++ b/src/components/explore/PriceHistoryGraph.vue @@ -105,6 +105,7 @@ export default { initialChart: { type: Object, required: true }, initialTimeFrame: { type: String, default: 'MAX' }, pairId: { type: String, default: null }, + tokenId: { type: String, default: null }, }, data() { return { @@ -201,6 +202,9 @@ export default { if (this.pairId) { options = { ...options, pairAddress: this.pairId }; } + if (this.tokenId) { + options = { ...options, tokenAddress: this.tokenId }; + } this.graph = await this.$store.dispatch('backend/fetchGraph', options); this.loading = false; }, diff --git a/src/views/TokenDetailView.vue b/src/views/TokenDetailView.vue index f20aff91..9c771697 100644 --- a/src/views/TokenDetailView.vue +++ b/src/views/TokenDetailView.vue @@ -6,7 +6,17 @@
- +
@@ -145,66 +155,12 @@ export default defineComponent({ }, pairTable: [], pairMap: new Map(), - tokenIdMap: new Map(), totalSupply: 0, loading: false, }; }, computed: { ...mapGetters(['activeNetwork']), - graphData() { - let reserve = new BigNumber(0); - return this.history.reduce( - (acc, tx) => { - reserve = reserve.plus(this.getDeltaReserve(tx)); - acc.datasets[0].data = [...acc.datasets[0].data, this.getUsdPrice(tx)].map((d) => d || 0); - acc.datasets[1].data = [ - ...acc.datasets[1].data, - new BigNumber(reserve) - .multipliedBy(this.getUsdPrice(tx)) - .div(new BigNumber(10).pow(this.tokenWithUsd.decimals)), - ].map((d) => d || 0); - acc.datasets[2].data = [ - ...acc.datasets[2].data, - new BigNumber(reserve).div(new BigNumber(10).pow(this.tokenWithUsd.decimals)), - ].map((d) => d || 0); - if (tx.type === 'SwapTokens') { - acc.datasets[3].data = [ - ...acc.datasets[3].data, - new BigNumber(this.getDeltaReserve(tx)) - .abs() - .multipliedBy(this.getUsdPrice(tx)) - .div(new BigNumber(10).pow(this.tokenWithUsd.decimals)), - ].map((d) => d || 0); - } else { - acc.datasets[3].data = [...acc.datasets[3].data, 0]; - } - acc.x = [...acc.x, tx.microBlockTime]; - return acc; - }, - { - x: [], - datasets: [ - { - label: 'Price', - data: [], - }, - { - label: 'TVL', - data: [], - }, - { - label: 'Locked', - data: [], - }, - { - label: 'Volume', - data: [], - }, - ], - }, - ); - }, reversedTransactions() { return this.history .slice() @@ -287,10 +243,6 @@ export default defineComponent({ token0: this.pairMap.get(pair.address).token0, token1: this.pairMap.get(pair.address).token1, })); - this.tokenIdMap = new Map([ - ...this.pairs.pairs0.map((pair) => [pair.address, 0]), - ...this.pairs.pairs1.map((pair) => [pair.address, 1]), - ]); // Fetch token price history this.history = await this.$store.dispatch('backend/fetchHistory', { @@ -300,21 +252,6 @@ export default defineComponent({ }, methods: { shortenAddress, - getDeltaReserve(historyEntry) { - if (this.tokenIdMap.get(historyEntry.pairAddress) === 0) { - return historyEntry.deltaReserve0; - } - return historyEntry.deltaReserve1; - }, - getTokenPrice(historyEntry) { - if (this.tokenIdMap.get(historyEntry.pairAddress) === 0) { - return historyEntry.token0AePrice; - } - return historyEntry.token1AePrice; - }, - getUsdPrice(historyEntry) { - return new BigNumber(this.getTokenPrice(historyEntry)).multipliedBy(historyEntry.aeUsdPrice); - }, }, }); From 6d2b296b0dccb758099f12cb05797f93070c3ade Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Thu, 29 Aug 2024 11:32:01 +0200 Subject: [PATCH 07/11] feat: adapt adjusted graph endpoint response --- src/components/explore/PriceHistoryGraph.vue | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/explore/PriceHistoryGraph.vue b/src/components/explore/PriceHistoryGraph.vue index 650aaec3..dacdfa37 100644 --- a/src/components/explore/PriceHistoryGraph.vue +++ b/src/components/explore/PriceHistoryGraph.vue @@ -118,7 +118,8 @@ export default { timeFrames: TIME_FRAMES, graph: { labels: [], - datasets: [], + data: [], + graphType: null, }, loading: false, }; @@ -164,12 +165,14 @@ export default { graphData() { return { labels: this.graph.labels.map((l) => Number(l)), - datasets: this.graph.datasets.map((d) => ({ - label: d.label, - data: d.data.map((n) => Number(n)), - borderColor: 'rgb(0 255 157 / 80%)', - backgroundColor: 'rgb(0 255 157 / 80%)', - })), + datasets: [ + { + label: this.graph.graphType, + data: this.graph.data?.map((n) => Number(n)), + borderColor: 'rgb(0 255 157 / 80%)', + backgroundColor: 'rgb(0 255 157 / 80%)', + }, + ], }; }, showBar() { @@ -194,7 +197,7 @@ export default { }, async fetchData() { this.loading = true; - this.graph.datasets = []; + this.graph.data = []; let options = { graphType: this.selectedChart.type, timeFrame: this.selectedTimeFrame, From f6ad8d6f7f63f950eeb9260481370f8bb08b2a34 Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Thu, 29 Aug 2024 15:07:31 +0200 Subject: [PATCH 08/11] feat: adapt adjusted graph types --- src/views/PoolDetailView.vue | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/views/PoolDetailView.vue b/src/views/PoolDetailView.vue index 5fd73b4f..ac531221 100644 --- a/src/views/PoolDetailView.vue +++ b/src/views/PoolDetailView.vue @@ -17,8 +17,14 @@ - {{ $t('poolDetail.swap') }}x + {{ $t('poolDetail.swap') }} Date: Mon, 18 Nov 2024 17:52:07 +0100 Subject: [PATCH 09/11] fix: calculation of pool share percentage on AddLiquidity --- src/views/AddLiquidity.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/AddLiquidity.vue b/src/views/AddLiquidity.vue index e87bb495..1e89fc29 100644 --- a/src/views/AddLiquidity.vue +++ b/src/views/AddLiquidity.vue @@ -153,7 +153,10 @@ export default { const amountTokenB = expandDecimals(this.amountTokenB, this.tokenB.decimals); const amount = amountTokenA * amountTokenB; const reserve = this.reserveTokenB * this.reserveTokenA; - return BigNumber(amount).times(100).div(reserve).toNumber(); + return BigNumber(amount) + .times(100) + .div(reserve + amount) + .toNumber(); }, ratio() { if (!this.reserveTokenA || !this.reserveTokenB || !this.tokenA || !this.tokenB) { From f7513b48e6414101532dc2a109d182c7548733c7 Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Mon, 18 Nov 2024 17:53:34 +0100 Subject: [PATCH 10/11] fix: improve calculation and display of price impact on Swap --- src/components/ConfirmSwapModal.vue | 2 +- src/lib/swapUtils.js | 2 +- src/locales/en.json | 2 +- src/locales/fr.json | 2 +- src/locales/ru.json | 2 +- src/locales/zh-cn.json | 2 +- tests/unit/swap-utils.spec.js | 24 ++++++++++++------------ 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/components/ConfirmSwapModal.vue b/src/components/ConfirmSwapModal.vue index 41080d3b..02bb7d65 100644 --- a/src/components/ConfirmSwapModal.vue +++ b/src/components/ConfirmSwapModal.vue @@ -44,7 +44,7 @@ {{ slippage }}%
- {{ $t('confirmSwapModal.priceImpact') }} + {{ $t('confirmSwapModal.priceImpact', { token: from.symbol }) }} {{ priceImpact?.toFixed(8) }}%
diff --git a/src/lib/swapUtils.js b/src/lib/swapUtils.js index aeedbff8..723c4234 100644 --- a/src/lib/swapUtils.js +++ b/src/lib/swapUtils.js @@ -119,7 +119,7 @@ const getPriceImpactForPairReserves = (pairReserves, amountA) => { const marketPrice = BigNumber(1).div(ratioFromPairReserves(pairReserves)); const newPrice = BigNumber(amountA).div(receivedB); - return newPrice.minus(marketPrice).times(100).div(marketPrice).toNumber(); + return -newPrice.minus(marketPrice).times(100).div(newPrice).toNumber(); }; /** diff --git a/src/locales/en.json b/src/locales/en.json index fd8d73b9..382b50ff 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -50,7 +50,7 @@ "to": "To", "transactionDetails": "Transaction Details", "liquidityProviderFee": "Liquidity Provider Fee", - "priceImpact": "Price Impact", + "priceImpact": "Price Impact on {token}", "allowedSlippage": "Allowed Slippage", "minReceived": "Minimum received", "maxSpent": "Maximum spent", diff --git a/src/locales/fr.json b/src/locales/fr.json index 1f7b00dd..5c1092f1 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -42,7 +42,7 @@ "to": "Pour", "transactionDetails": "détails de la transaction", "liquidityProviderFee": "Frais de fournisseur de liquidité", - "priceImpact": "Incidence sur les prix", + "priceImpact": "Incidence sur les prix de {token}", "allowedSlippage": "Glissement autorisé", "minReceived": "Minimum reçu", "maxSpent": "Dépense maximale", diff --git a/src/locales/ru.json b/src/locales/ru.json index ca1d72ea..3d6cdecb 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -36,7 +36,7 @@ "to": "Получаете", "transactionDetails": "Детали транзакции", "liquidityProviderFee": "Комиссия поставщика ликвидности", - "priceImpact": "Влияние на цену", + "priceImpact": "Влияние на цену {token}", "allowedSlippage": "Допустимое проскальзывание", "minReceived": "Минимум к получению", "maxSpent": "Максимум к продаже", diff --git a/src/locales/zh-cn.json b/src/locales/zh-cn.json index 6ddf476c..c4f6e0fb 100644 --- a/src/locales/zh-cn.json +++ b/src/locales/zh-cn.json @@ -49,7 +49,7 @@ "to": "到", "transactionDetails": "交换详情", "liquidityProviderFee": "Liquidity Provider Fee", - "priceImpact": "价格影响", + "priceImpact": "价格影响针对 {token}", "allowedSlippage": "滑点", "minReceived": "最小收益", "maxSpent": "最多支付", diff --git a/tests/unit/swap-utils.spec.js b/tests/unit/swap-utils.spec.js index 14406756..9177d011 100644 --- a/tests/unit/swap-utils.spec.js +++ b/tests/unit/swap-utils.spec.js @@ -70,7 +70,7 @@ describe('route price impact', () => { }); const toPairs = (xs) => xs.map(toPair); const priceImpact = (xs, tokenA, amountA) => getPriceImpactForRoute(toPairs(xs), tokenA, amountA); - it('price impact to be 0.5', () => { + it('price impact to be -0.4975124378109453', () => { expect( priceImpact( [ @@ -82,9 +82,9 @@ describe('route price impact', () => { 'a', 10000n, ), - ).toBe(0.5); + ).toBe(-0.4975124378109453); }); - it('price impact to be 5', () => { + it('price impact to be -4.761904761904762', () => { expect( priceImpact( [ @@ -96,7 +96,7 @@ describe('route price impact', () => { 'a', 100000n, ), - ).toBe(5); + ).toBe(-4.761904761904762); }); it('should receive 1', () => { expect(getReceivedTokensForPairReserves([[2, 2]], 2).toNumber()).toBe(1); @@ -113,7 +113,7 @@ describe('route price impact', () => { ).toBe(0.6666666666666666); }); - it('swapping reserveA will have priceImpact=100%', () => { + it('swapping reserveA will have priceImpact=-50%', () => { expect( priceImpact( [ @@ -125,10 +125,10 @@ describe('route price impact', () => { 'a', 2, ), - ).toBe(100); + ).toBe(-50); }); - it('swapping reserveA withing two pairs will have priceImpact=200%', () => { + it('swapping reserveA withing two pairs will have priceImpact=-66.66666666666667%', () => { expect( priceImpact( [ @@ -144,9 +144,9 @@ describe('route price impact', () => { 'a', 2, ), - ).toBe(200); + ).toBe(-66.66666666666667); }); - it('swapping reserveA withing 3 pairs will have priceImpact=300%', () => { + it('swapping reserveA withing 3 pairs will have priceImpact=-75%', () => { expect( priceImpact( [ @@ -166,9 +166,9 @@ describe('route price impact', () => { 'a', 2, ), - ).toBe(300); + ).toBe(-75); }); - it('swapping 25 for [[100,50],[25,25]] will have priceImpact=75%', () => { + it('swapping 25 for [[100,50],[25,25]] will have priceImpact=-42.857142857142854%', () => { expect( priceImpact( [ @@ -184,7 +184,7 @@ describe('route price impact', () => { 'a', 25, ), - ).toBe(75); + ).toBe(-42.857142857142854); }); }); describe('get route reserves', () => { From 97fee406f536649910cd06fe9974fd90f317fe77 Mon Sep 17 00:00:00 2001 From: Timo Erdelt Date: Tue, 19 Nov 2024 17:08:26 +0100 Subject: [PATCH 11/11] fix: increase timeout of e2e tests --- cypress.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cypress.config.js b/cypress.config.js index bfc84190..553377ac 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -13,4 +13,5 @@ export default defineConfig({ 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1', viewportHeight: 800, viewportWidth: 400, + defaultCommandTimeout: 20000, });