From 2177c9070027d30640779426e996e6ac9a1b90da Mon Sep 17 00:00:00 2001 From: alter-eggo Date: Tue, 4 Jul 2023 20:41:39 +0400 Subject: [PATCH] feat: add layer zero tx links --- package-lock.json | 23 ++++++-- package.json | 3 +- .../CrossChainBoostCards.vue | 40 +++++++++---- .../cross-chain-boost/SyncFinalState.vue | 29 ++++++++- .../cross-chain-boost/SyncNetworkAction.vue | 12 +++- src/providers/cross-chain-sync.provider.ts | 59 +++++++++++++++++++ 6 files changed, 143 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 76b3d68afe..3dd700dae0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@balancer/frontend-v2", "version": "1.108.8", "license": "MIT", + "dependencies": { + "@layerzerolabs/scan-client": "^0.0.5" + }, "devDependencies": { "@aave/protocol-js": "^4.3.0", "@balancer-labs/assets": "github:balancer-labs/assets#master", @@ -4775,6 +4778,14 @@ "node": ">= 8.0.0" } }, + "node_modules/@layerzerolabs/scan-client": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@layerzerolabs/scan-client/-/scan-client-0.0.5.tgz", + "integrity": "sha512-qhqYJx0kH4NHxEEOasrZJnoCEIRVs8aFF2Vkn3UG9FplAuj/eEj9jIJyLZNP1F1qz80vy9349Vd0kJcFcetV0Q==", + "peerDependencies": { + "axios": "*" + } + }, "node_modules/@metamask/detect-provider": { "version": "1.2.0", "dev": true, @@ -8111,7 +8122,6 @@ }, "node_modules/axios": { "version": "0.21.2", - "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.14.0" @@ -12439,7 +12449,6 @@ }, "node_modules/follow-redirects": { "version": "1.15.2", - "dev": true, "funding": [ { "type": "individual", @@ -28329,6 +28338,12 @@ "path-to-regexp": "^6.1.0" } }, + "@layerzerolabs/scan-client": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@layerzerolabs/scan-client/-/scan-client-0.0.5.tgz", + "integrity": "sha512-qhqYJx0kH4NHxEEOasrZJnoCEIRVs8aFF2Vkn3UG9FplAuj/eEj9jIJyLZNP1F1qz80vy9349Vd0kJcFcetV0Q==", + "requires": {} + }, "@metamask/detect-provider": { "version": "1.2.0", "dev": true @@ -30854,7 +30869,6 @@ }, "axios": { "version": "0.21.2", - "dev": true, "requires": { "follow-redirects": "^1.14.0" } @@ -34010,8 +34024,7 @@ "dev": true }, "follow-redirects": { - "version": "1.15.2", - "dev": true + "version": "1.15.2" }, "for-each": { "version": "0.3.3", diff --git a/package.json b/package.json index f5623d86cf..d3e3c57ff0 100644 --- a/package.json +++ b/package.json @@ -159,7 +159,8 @@ "wait-for-expect": "^3.0.2", "walletlink": "^2.1.5", "web3-utils": "^1.3.1", - "worker-loader": "^3.0.8" + "worker-loader": "^3.0.8", + "@layerzerolabs/scan-client": "^0.0.5" }, "lint-staged": { "*.{js,ts,vue}": "eslint --cache --fix --max-warnings 0", diff --git a/src/components/contextual/pages/vebal/cross-chain-boost/CrossChainBoostCards.vue b/src/components/contextual/pages/vebal/cross-chain-boost/CrossChainBoostCards.vue index 488eea80e3..68d6474ac8 100644 --- a/src/components/contextual/pages/vebal/cross-chain-boost/CrossChainBoostCards.vue +++ b/src/components/contextual/pages/vebal/cross-chain-boost/CrossChainBoostCards.vue @@ -31,6 +31,8 @@ const { infoMessage, hasError, showingUnsyncedNetworks, + getLayerZeroTxLink, + syncLayerZeroTxLinks, } = useCrossChainSync(); const { fNum } = useNumbers(); const { veBalBalance } = useVeBal(); @@ -39,7 +41,6 @@ const { veBalBalance } = useVeBal(); * STATE */ const isSyncModalOpen = ref(false); - /** * COMPUTED */ @@ -67,6 +68,7 @@ function checkIfNetworkSyncing(network: Network) { } function onCloseModal() { isSyncModalOpen.value = false; + void getLayerZeroTxLink(''); } @@ -136,19 +138,35 @@ function onCloseModal() { > -
- {{ getLoadingTooltipText(network) }} +
+ + {{ getLoadingTooltipText(network) }} + + + + Click this icon to view Layerzero transaction +
diff --git a/src/components/contextual/pages/vebal/cross-chain-boost/SyncFinalState.vue b/src/components/contextual/pages/vebal/cross-chain-boost/SyncFinalState.vue index 2b1152fcc3..3eb28bc657 100644 --- a/src/components/contextual/pages/vebal/cross-chain-boost/SyncFinalState.vue +++ b/src/components/contextual/pages/vebal/cross-chain-boost/SyncFinalState.vue @@ -1,11 +1,14 @@ @@ -23,7 +26,9 @@ defineEmits(['close']);
{{ $t('crossChainBoost.syncInitiatedModal.description') }}
-
+
@@ -39,14 +44,32 @@ defineEmits(['close']);
{{ configs[network].chainName }}
-
+
{{ veBalBalance }} veBAL
+ +
+ + + +
diff --git a/src/components/contextual/pages/vebal/cross-chain-boost/SyncNetworkAction.vue b/src/components/contextual/pages/vebal/cross-chain-boost/SyncNetworkAction.vue index 82b4c01fb6..4d707fdcef 100644 --- a/src/components/contextual/pages/vebal/cross-chain-boost/SyncNetworkAction.vue +++ b/src/components/contextual/pages/vebal/cross-chain-boost/SyncNetworkAction.vue @@ -32,8 +32,13 @@ const emit = defineEmits(['update:activeTabIdx']); const { t } = useI18n(); const { addTransaction } = useTransactions(); const { txListener } = useEthers(); -const { l2VeBalBalances, sync, setTempSyncingNetworks, tempSyncingNetworks } = - useCrossChainSync(); +const { + l2VeBalBalances, + sync, + setTempSyncingNetworks, + tempSyncingNetworks, + setSyncTxHashes, +} = useCrossChainSync(); /** * STATE @@ -57,6 +62,7 @@ async function handleTransaction( txListener(tx, { onTxConfirmed: (receipt: TransactionReceipt) => { console.log('Receipt', receipt); + setSyncTxHashes(network, tx.hash); }, onTxFailed: () => { // @@ -75,7 +81,7 @@ async function handleAction(network: Network) { 'tempSyncingNetworks', JSON.stringify(tempSyncingNetworks.value) ); - + setSyncTxHashes(network, tx.hash); return tx; } catch (error) { console.error(error); diff --git a/src/providers/cross-chain-sync.provider.ts b/src/providers/cross-chain-sync.provider.ts index 99f1ea6cae..b7e2faece5 100644 --- a/src/providers/cross-chain-sync.provider.ts +++ b/src/providers/cross-chain-sync.provider.ts @@ -14,6 +14,8 @@ import configs from '@/lib/config'; import { GaugeWorkingBalanceHelper } from '@/services/balancer/contracts/contracts/gauge-working-balance-helper'; import { LiquidityGauge } from '@/services/balancer/contracts/contracts/liquidity-gauge'; import { useI18n } from 'vue-i18n'; +import { getMessagesBySrcTxHash } from '@layerzerolabs/scan-client'; +import { retryPromiseWithDelay } from '@/lib/utils/promise'; export enum NetworkSyncState { Unsynced = 'Unsynced', @@ -34,6 +36,10 @@ export interface TempSyncingNetworks { syncTimestamp?: number; } +export interface SyncTxHashes { + [key: string]: string; +} + // all networks that are supported by cross-chain sync feature export const veBalSyncSupportedNetworks = Object.keys(configs) .filter(key => configs[Number(key)].supportsVeBalSync) @@ -54,6 +60,14 @@ export const crossChainSyncProvider = () => { syncingNetworksFromStorage ? JSON.parse(syncingNetworksFromStorage) : {} ); + const syncTxHashesFromStorage = localStorage.getItem('syncTxHashes'); + + const syncTxHashes = ref>( + syncTxHashesFromStorage ? JSON.parse(syncTxHashesFromStorage) : {} + ); + + const syncLayerZeroTxLinks = ref>({}); + /** * omniVotingEscrowLocks contains the user's veBAL data that is known by the bridge contract * it is used to determine sync status to l2 networks @@ -270,6 +284,15 @@ export const crossChainSyncProvider = () => { return tempSyncingNetworks.value; } + async function setSyncTxHashes(network: Network, txHash: string) { + syncTxHashes.value[account.value] = { + ...syncTxHashes.value[account.value], + [network]: txHash, + }; + + localStorage.setItem('syncTxHashes', JSON.stringify(syncTxHashes.value)); + } + function clearTempSyncingNetworksFromSynced() { if (!tempSyncingNetworks.value[account.value]) return; @@ -313,6 +336,20 @@ export const crossChainSyncProvider = () => { }); } + async function getLayerZeroTxLink(txHash: string) { + const { messages } = await getMessagesBySrcTxHash(101, txHash); + const message = messages[0]; + + if (!message) { + throw new Error('No message found in Layer Zero'); + } + + const { srcUaAddress, dstUaAddress, dstChainId, srcUaNonce } = message; + const link = `https://layerzeroscan.com/101/address/${srcUaAddress}/message/${dstChainId}/address/${dstUaAddress}/nonce/${srcUaNonce}`; + + return link; + } + watch( () => networksBySyncState.value, newVal => { @@ -335,6 +372,24 @@ export const crossChainSyncProvider = () => { } ); + watch( + () => [syncTxHashes.value, account.value], + async values => { + const val = values[0]; + if (!val || !val[account.value]) return; + + for (const network of Object.keys(val[account.value])) { + const hash = syncTxHashes.value[account.value][network]; + syncLayerZeroTxLinks.value[network] = await retryPromiseWithDelay( + getLayerZeroTxLink(hash), + 3, + 2000 + ); + } + }, + { immediate: true, deep: true } + ); + return { showingUnsyncedNetworks, hasError, @@ -352,6 +407,10 @@ export const crossChainSyncProvider = () => { infoMessage, getGaugeWorkingBalance, triggerGaugeUpdate, + getLayerZeroTxLink, + syncTxHashes, + setSyncTxHashes, + syncLayerZeroTxLinks, }; };