Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#884, #902 | multichain resolution #904

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { computed, onMounted } from 'vue';
import { computed, onMounted, watch } from 'vue';
import { useStore } from 'vuex';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
Expand All @@ -8,15 +8,17 @@ import moment from 'moment';
import { getCore, useChainStore } from 'src/core';
import { TELOS_NETWORK_NAMES } from 'src/core/mocks/chain-constants';
import { providerManager } from 'src/boot/evm';
import { useRoute } from 'vue-router';

const $store = useStore();
const { t: $t } = useI18n();
const $q = useQuasar();
const $route = useRoute();

// computed
const isNative = computed(() => $store.getters['login/isNative']);

onMounted(async () => {
const checkNetworkHealth = async () => {
const network = useChainStore().currentChain.settings.getNetwork();
if (TELOS_NETWORK_NAMES.includes(network)) {
const script = document.createElement('script');
Expand All @@ -27,7 +29,13 @@ onMounted(async () => {
document.body.appendChild(script);
}
const indexerApi = useChainStore().currentChain.settings.getIndexerApi();
const chainName = useChainStore().currentChain.settings.getDisplay();
const theme = $q.dark.isActive ? useChainStore().currentChain.settings.getThemes().dark : useChainStore().currentChain.settings.getThemes().light;
const health = await indexerApi.get('/v1/health');
const background = theme?.primary || '#0099FF';
const color = theme?.color || 'white';
// print in console with background #8B3F98 and white text the following message: Using indexer {health.data.version} with {health.data.secondsBehind} seconds behind
console.debug(`%cUsing indexer ${health.data.version} for '${chainName}' with ${health.data.secondsBehind} seconds behind`, `background: ${background}; color: ${color};`);

if (health.data?.secondsBehind > 3) {
let behindBy = moment(health.data.secondsBehind * 1000).utc().format('HH:mm:ss');
Expand All @@ -52,6 +60,10 @@ onMounted(async () => {
html: true,
});
}
};

onMounted(async () => {
// await checkNetworkHealth();

// On login we must set the address and record the provider
getCore().events.onLoggedOut.subscribe(() => {
Expand All @@ -70,7 +82,17 @@ onMounted(async () => {
});
});

// Watch for changes in the route query to react when network changes
watch(
() => $route.query.network,
async (newNetwork, oldNetwork) => {
if (newNetwork !== oldNetwork) {
await checkNetworkHealth();
}
},
);
</script>

<template>
<div id="q-app">
<router-view />
Expand Down
3 changes: 2 additions & 1 deletion src/boot/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export default boot(({ app }) => {
initCore(app);

const defaultNetwork = Object.keys(evmSettings)[0];
let network = new URLSearchParams(window.location.search).get('network');
let network = new URLSearchParams(window.location.search).get('network') ?? process.env.NETWORK_EVM_NAME;
console.log('Multichain initMultichain()', { network, NETWORK_EVM_NAME: process.env.NETWORK_EVM_NAME });
if (network) {
const exists = Object.keys(evmSettings).some(key => evmSettings[key].getNetwork() === network);
if (!exists) {
Expand Down
6 changes: 3 additions & 3 deletions src/components/InternalTransactionFlatTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ export default {
let processedTransactions = 0;
let lastTransactionHash = '';
const totalEntries = [];
result.results.forEach((internalTrx) => {
result.data.results.forEach((internalTrx) => {
if (internalTrx.transactionHash !== lastTransactionHash) {
processedTransactions++;
lastTransactionHash = internalTrx.transactionHash;
Expand Down Expand Up @@ -250,9 +250,9 @@ export default {

const filter = Object.assign({}, this.filter ? this.filter : {});
if (this.address) {
path = `/address/${this.address}/internal?limit=${limit}`;
path = `v1/address/${this.address}/internal?limit=${limit}`;
} else {
path = `/internal?limit=${limit}`;
path = `v1/internal?limit=${limit}`;
}

if (filter.block) {
Expand Down
1 change: 0 additions & 1 deletion src/components/LoginModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useStore } from 'vuex';
import { useI18n } from 'vue-i18n';
import detectEthereumProvider from '@metamask/detect-provider';
import { Authenticator } from 'universal-authenticator-library';

import {
LOGIN_EVM,
LOGIN_NATIVE,
Expand Down
68 changes: 7 additions & 61 deletions src/components/header/AppHeaderLinks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';

import {
TELOSCAN_MAINNET_URL,
TELOSCAN_TESTNET_URL,
} from 'src/lib/chain-utils';

import LanguageSwitcherModal from 'components/header/LanguageSwitcherModal.vue';
import OutlineButton from 'components/OutlineButton.vue';
import { useChainStore } from 'src/core';
import { HeaderMenuEntry } from 'src/core/types';
import { chains, multichainSelectedNetwork, switchChain } from 'src/lib/multichain-utils';

const $route = useRoute();
const $router = useRouter();
Expand All @@ -24,17 +20,6 @@ defineProps<{
}>();
const emit = defineEmits(['close-menu']);

const networksMenuItems = {
mainnet: [{
url: TELOSCAN_MAINNET_URL,
label: 'Telos Mainnet',
}],
testnet: [{
url: TELOSCAN_TESTNET_URL,
label: 'Telos Testnet',
}],
};

const blockchainMenuExpandedMobile = ref(false);
const developersMenuExpandedMobile = ref(false);
const moreMenuExpandedMobile = ref(false);
Expand Down Expand Up @@ -105,28 +90,6 @@ function toggleDarkMode() {
$q.dark.toggle();
localStorage.setItem('darkModeEnabled', $q.dark.isActive.toString());
}

function getIsCurrentNetworkMenuItem(url: string) {
networksMenuItems.mainnet.forEach((item) => {
if (item.url === url) {
return true;
}
});
return false;
}

function goTo(to: string | { name: string }) {
blurActiveElement();
closeAllMenus();

const httpsRegex = /^https/;
if (typeof to === 'string' && httpsRegex.test(to)) {
window.open(to, '_blank');
return;
}

$router.push(to);
}
</script>

<template>
Expand Down Expand Up @@ -249,35 +212,18 @@ function goTo(to: string | { name: string }) {
}"
>
<li
v-for="item in networksMenuItems.mainnet"
:key="`networks-submenu-item-mainnet-${item.label}`"
:class="{
'c-header-links__submenu-li': true,
'c-header-links__submenu-li--current': getIsCurrentNetworkMenuItem(item.url),
}"
tabindex="0"
role="link"
@click="goTo(item.url)"
@keydown.enter="goTo(item.url)"
>
{{ item.label }}
</li>

<q-separator />

<li
v-for="item in networksMenuItems.testnet"
:key="`networks-submenu-item-testnet-${item.label}`"
v-for="chain in chains"
:key="chain.network"
:class="{
'c-header-links__submenu-li': true,
'c-header-links__submenu-li--current': getIsCurrentNetworkMenuItem(item.url),
'c-header-links__submenu-li--current': multichainSelectedNetwork?.network === chain.network
}"
tabindex="0"
role="link"
@click="goTo(item.url)"
@keydown.enter="goTo(item.url)"
@click="switchChain(chain)"
@keydown.enter="switchChain(chain)"
>
{{ item.label }}
{{ chain.settings.getDisplay() }}
</li>
</ul>
</li>
Expand Down
85 changes: 5 additions & 80 deletions src/components/header/AppHeaderTopBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,22 @@ import {
onBeforeMount,
onBeforeUnmount,
onMounted,
ref,
watch,
} from 'vue';
import { useQuasar } from 'quasar';
import { useStore } from 'vuex';
import { useI18n } from 'vue-i18n';
import { formatUnits } from 'ethers/lib/utils';
import { useRoute, useRouter } from 'vue-router';
import { TeloscanEVMChainSettings, evmSettings, useChainStore } from 'src/core';
import { useRoute } from 'vue-router';
import { useChainStore } from 'src/core';


import AppHeaderWallet from 'components/header/AppHeaderWallet.vue';
import OutlineButton from 'components/OutlineButton.vue';
import AppSearch from 'components/AppSearch.vue';
import { CURRENT_CONTEXT } from 'src/core/mocks';
import { chains, multichainSelectedNetwork, switchChain } from 'src/lib/multichain-utils';

const $route = useRoute();
const $router = useRouter();

const $q = useQuasar();
const $store = useStore();
const $i18n = useI18n();
Expand Down Expand Up @@ -90,57 +88,6 @@ function toggleDarkMode() {
localStorage.setItem('darkModeEnabled', $q.dark.isActive.toString());
}


// Multichain
interface ChainOption {
network: string;
settings: TeloscanEVMChainSettings;
}

const chains = [
{
network: 'telos-evm',
settings: evmSettings['telos-evm'],
},
{
network: 'telos-evm-testnet',
settings: evmSettings['telos-evm-testnet'],
},
] as ChainOption[];

const selectedNetwork = ref<ChainOption | undefined>(undefined);
const switchChain = (network: ChainOption) => {
selectedNetwork.value = network;
};

onMounted(() => {
const defaultNetwork = Object.keys(evmSettings)[0];
let network = new URLSearchParams(window.location.search).get('network');
if (network) {
const exists = Object.keys(evmSettings).some(key => evmSettings[key].getNetwork() === network);
if (!exists) {
network = defaultNetwork;
}
} else {
network = defaultNetwork;
}
$router.replace({ query: { ...$route.query, network } });
selectedNetwork.value = chains.find(chain => chain.network === network);
});

watch(selectedNetwork, () => {
if (selectedNetwork.value) {
chainStore.setChain(CURRENT_CONTEXT, selectedNetwork.value.network);
// replace the url to reflect the network
$router.replace({ query: { ...$route.query, network: selectedNetwork.value.network } });
}
});

watch(() => chainStore.currentChain, (currentChain) => {
selectedNetwork.value = chains.find(chain => chain.network === currentChain.settings.getNetwork());
});


</script>

<template>
Expand Down Expand Up @@ -189,28 +136,6 @@ watch(() => chainStore.currentChain, (currentChain) => {

<q-menu>
<q-list>
<!--q-item
clickable
role="link"
@click="goToTeloscanMainnet"
@keydown.enter="goToTeloscanMainnet"
>
<q-item-section :class="chainStore.currentChain.settings.isTestnet() ? '' : 'text-primary'">
Telos Mainnet
</q-item-section>
</q-item>
<q-separator />
<q-item
clickable
role="link"
@click="goToTeloscanTestnet"
@keydown.enter="goToTeloscanTestnet"
>
<q-item-section :class="chainStore.currentChain.settings.isTestnet() ? 'text-primary' : ''">
Telos Testnet
</q-item-section>
</q-item-->

<template v-for="chain in chains" :key="chain.network">
<q-separator/>
<q-item
Expand All @@ -219,7 +144,7 @@ watch(() => chainStore.currentChain, (currentChain) => {
@click="switchChain(chain)"
@keydown.enter="switchChain(chain)"
>
<q-item-section :class="selectedNetwork?.network === chain.network ? 'text-primary' : ''">
<q-item-section :class="multichainSelectedNetwork?.network === chain.network ? 'text-primary' : ''">
<div class="c-header-top-bar__chain-option">
<img :src="chain.settings.getSmallLogoPath()" height="24" width="24">
<span>{{ chain.settings.getDisplay() }}</span>
Expand Down
5 changes: 3 additions & 2 deletions src/core/chains/EVMChainSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export default abstract class EVMChainSettings implements ChainSettings {
}

async init(): Promise<void> {
console.log('init');
this.trace('init');
// this is called only when this chain is needed to avoid initialization of all chains
if (this.ready) {
Expand Down Expand Up @@ -224,12 +225,12 @@ export default abstract class EVMChainSettings implements ChainSettings {
};

// update indexer health state
promise.then((state) => {
promise.then((state:IndexerHealthResponse) => {
this._indexerHealthState.state = state;
this.indexerChecked$.next(true);
});

return promise;
return promise as Promise<IndexerHealthResponse>;
}

/**
Expand Down
8 changes: 6 additions & 2 deletions src/core/mocks/CoreConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import { App } from 'vue';
import { Authenticator, RpcEndpoint } from 'universal-authenticator-library';
import { Subject } from 'rxjs';
import { AccountModel } from 'src/core/mocks/AccountStore';
import { ChainModel, TeloscanEVMChainSettings, useChainStore } from './ChainStore';
import { CURRENT_CONTEXT } from '.';
import {
ChainModel,
CURRENT_CONTEXT,
TeloscanEVMChainSettings,
useChainStore,
} from 'src/core/mocks';
import { ethers } from 'ethers';

export interface ComplexMessage {
Expand Down
Loading
Loading