Skip to content

Commit

Permalink
feat: Add Phalcon Explorer shortcut for Solana txns
Browse files Browse the repository at this point in the history
  • Loading branch information
0xbe37e committed Jun 20, 2024
1 parent 9d26963 commit 5a4c515
Show file tree
Hide file tree
Showing 35 changed files with 563 additions and 86 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
### v5.1.0

- [feat] Add shortcut to navigate Solana transaction hashes to Phalcon Explorer
- [fix] Fix extension functionality issue caused by solana.fm website update

### v5.0.7

- [fix] Remove the copy button for the new version of Etherscan transaction hash, and readapt some of the copy block buttons
- [feat] Adapted to the new version of basescan
- [fix] Remove the copy button for the new version of Etherscan transaction hash, and readapt some of the copy block buttons

### v5.0.6

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "metasuites",
"version": "5.0.7",
"version": "5.1.0",
"repository": {
"type": "git",
"url": "https://github.com/blocksecteam/metasuites.git"
Expand Down
23 changes: 18 additions & 5 deletions src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
GET_SOLANAFM_ACCOUNT_INFO,
GET_SOLANAFM_ACCOUNT_TRANSFERS,
GET_SOLSCAN_ACCOUNT_TAB_DATA,
GET_SOLSCAN_TRANSACTION
GET_SOLSCAN_TRANSACTION,
GET_SOLSCAN_BLOCK_TXS
} from '@common/constants'

import { initBackgroundRequest } from './listeners'
Expand Down Expand Up @@ -168,9 +169,11 @@ browser.webRequest.onCompleted.addListener(
'https://api.solscan.io/v2/account/stake?*',
'https://api-v2.solscan.io/v2/account/activity/dextrading?*',
'https://api-v2.solscan.io/v2/token/transfer?address=*',
'https://api-v2.solscan.io/v2/account/transfer?address=*',
'https://api.solscan.io/v2/account/transaction?address=*',
'https://api.solscan.io/v2/token/holders?token=*',
'https://api-v2.solscan.io/v2/token/activity/dextrading/total?address=*'
'https://api-v2.solscan.io/v2/token/activity/dextrading/total?address=*',
'https://api.solscan.io/v2/account/token/txs?address=*'
]
}
)
Expand All @@ -193,6 +196,18 @@ browser.webRequest.onCompleted.addListener(
async details => {
const { tabId, method } = details
if (tabId && method === 'GET') {
browser.tabs.sendMessage(tabId, GET_SOLSCAN_BLOCK_TXS).catch(() => void 0)
}
},
{
urls: ['https://api.solscan.io/v2/block/txs?*']
}
)

browser.webRequest.onCompleted.addListener(
async details => {
const { tabId, method } = details
if (tabId && method === 'POST') {
browser.tabs
.sendMessage(tabId, GET_SOLANAFM_ACCOUNT_INFO)
.catch(() => void 0)
Expand All @@ -213,9 +228,7 @@ browser.webRequest.onCompleted.addListener(
}
},
{
urls: [
'https://api.solana.fm/v0/accounts/BNLtpXLqsjDGxzB1Mmcv3NmEiQhSSWFq8JViKrrrQ8Do/transfers?*'
]
urls: ['https://api.solana.fm/v0/accounts/*/transfers?*']
}
)

Expand Down
2 changes: 1 addition & 1 deletion src/common/components/icon/IconPhalcon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export default function IconPhalcon({
>
<rect width="16" height="16" rx="8" fill="#101820" />
<path
d="M5.33795 6.73452C6.52501 6.16582 7.31087 5.89413 7.9419 5.676C8.83293 5.36827 9.42014 5.16475 10.6092 4.30294C10.0366 4.04293 9.40066 3.8988 8.73166 3.8988C6.60876 3.8988 4.82573 5.35172 4.32324 7.31784C4.64265 7.10068 4.98153 6.90591 5.33892 6.73452H5.33795Z"
d="M5.33795 6.73464C6.52501 6.16594 7.31087 5.89425 7.9419 5.67612C8.83293 5.3684 9.42014 5.16487 10.6092 4.30306C10.0366 4.04305 9.40066 3.89893 8.73166 3.89893C6.60876 3.89893 4.82573 5.35185 4.32324 7.31796C4.64265 7.1008 4.98153 6.90603 5.33892 6.73464H5.33795Z"
fill="white"
/>
<path
Expand Down
1 change: 1 addition & 0 deletions src/common/constants/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ export const TRONSCAN_MULTI_SEARCH = 'tronscanMultiSearch'
export const GET_SOLSCAN_ACCOUNT_INFO = 'getSolscanAccountInfo'
export const GET_SOLSCAN_ACCOUNT_TAB_DATA = 'getSolscanAccountTabData'
export const GET_SOLSCAN_TRANSACTION = 'getSolscanTransaction'
export const GET_SOLSCAN_BLOCK_TXS = 'getSolscanBlockTxList'
export const GET_SOLANAFM_ACCOUNT_INFO = 'getSolanaFMAccountInfo'
export const GET_SOLANAFM_ACCOUNT_TRANSFERS = 'getSolanaFMAccountTransfers'
6 changes: 5 additions & 1 deletion src/common/constants/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const TRONSCAN_PAGE_NAMES = [

export const BLOCKSCOUT_PAGE_NAMES = ['TX', 'ADDRESS'] as const
export const MERLINSCAN_PAGE_NAMES = ['TX'] as const
export const SOLSCAN_PAGE_NAMES = ['ACCOUNT', 'TX', 'TOKEN'] as const
export const SOLSCAN_PAGE_NAMES = ['ACCOUNT', 'TX', 'TOKEN', 'BLOCK'] as const
export const SOLANAFM_PAGE_NAMES = ['ADDRESS', 'TX'] as const
export const SOLANAEXPL_PAGE_NAMES = ['ADDRESS', 'TX'] as const
export const ARKHAM_PAGE_NAMES = ['ADDRESS', 'TX'] as const
Expand Down Expand Up @@ -178,6 +178,10 @@ export const SOLSCAN_PAGES: Record<
TX: {
name: 'TX',
pattern: /^\/tx\/([1-9A-Za-z]+)/i
},
BLOCK: {
name: 'BLOCK',
pattern: /^\/block\/.+/
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/common/constants/support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,11 @@ export const PHALCON_SUPPORT_LIST = [
pathname: 'zksync-era',
chain: 'era.zksync',
supportSimulator: false
},
{
pathname: 'solana',
chain: 'solana',
supportSimulator: false
}
]

Expand Down
34 changes: 34 additions & 0 deletions src/content/solanaexpl/components/ParsersButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { type FC } from 'react'

import { PHALCON_EXPLORER_DOMAIN } from '@common/config/uri'
import { getChainSimpleName } from '@common/utils'
import { PHALCON_SUPPORT_LIST } from '@common/constants'
import { IconPhalcon } from '@common/components'

interface Props {
txHash: string
}

const ParsersBtn: FC<Props> = ({ txHash }) => {
const chain = getChainSimpleName()

const pathname = PHALCON_SUPPORT_LIST.find(
item => item.chain === chain
)?.pathname

const handleClick = (e: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
e.preventDefault()
window.open(`${PHALCON_EXPLORER_DOMAIN}/tx/${pathname}/${txHash}`, '_blank')
}

if (!chain) return null
return (
<IconPhalcon
mode="dark"
style={{ verticalAlign: 'sub' }}
onClick={handleClick}
/>
)
}

export default ParsersBtn
1 change: 1 addition & 0 deletions src/content/solanaexpl/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as PhalconExplorerButton } from './PhalconExplorerButton'
export { default as FundFlowButton } from './FundFlowButton'
export { default as MainAddressLabel } from './MainAddressLabel'
export { default as MainPrivateLabel } from './MainPrivateLabel'
export { default as ParsersButton } from './ParsersButton'
2 changes: 2 additions & 0 deletions src/content/solanaexpl/feat-scripts/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { default as renderFundFlowButton } from './fund-flow'
export { default as renderMainAddressLabel } from './main-address-label'
export { default as renderTxPageAddressLabels } from './tx-address-labels'
export { default as renderAlternativeParsers } from './quick2parsers'
export { default as renderTransactionHashPhalconLink } from './transaction-hash-phalcon-link'
17 changes: 17 additions & 0 deletions src/content/solanaexpl/feat-scripts/quick2parsers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createRoot } from 'react-dom/client'
import $ from 'jquery'

import { ParsersButton } from '../components'

const renderAlternativeParsers = async () => {
const container = $(
'.card table .list tr:first-of-type > td:last-child > div'
).first()
const txHash = container.text().trim()
const rootEl = $('<span class="font-size-tiny me-2"></span>')
container.prepend(rootEl)

createRoot(rootEl[0]).render(<ParsersButton txHash={txHash} />)
}

export default renderAlternativeParsers
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { type FC, type ReactNode } from 'react'
import isMobile from 'is-mobile'
import { createRoot } from 'react-dom/client'

import { PHALCON_SUPPORT_LIST } from '@common/constants'
import { getChainSimpleName } from '@common/utils'
import { PHALCON_EXPLORER_DOMAIN } from '@common/config/uri'
import { IconPhalcon } from '@common/components'

const PhalconExplorerButton: FC<{ hash: string }> = ({ hash }) => {
const chain = getChainSimpleName()

const pathname = PHALCON_SUPPORT_LIST.find(
item => item.chain === chain
)?.pathname

const handleClick = (e: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
e.preventDefault()
window.open(`${PHALCON_EXPLORER_DOMAIN}/tx/${pathname}/${hash}`, '_blank')
}

if (!chain) return null
return <IconPhalcon mode="dark" onClick={handleClick} />
}

const appendIconToElement = (el: HTMLElement, reactNode: ReactNode) => {
if (!isMobile()) {
el.onmouseover = () => {
const btnEls = el.querySelectorAll<HTMLElement>(
'.__metadock-copy-address-btn__'
)
if (btnEls.length) {
btnEls.forEach(btnEl => {
btnEl.style.display = 'inline-flex'
})
}
}
el.onmouseout = () => {
const btnEls = el.querySelectorAll<HTMLElement>(
'.__metadock-copy-address-btn__'
)
if (btnEls.length) {
btnEls.forEach(btnEl => {
btnEl.style.display = 'none'
})
}
}
}

el.setAttribute(
'style',
'padding-right:20px;position:relative;width:fit-content;'
)

const rootEl = document.createElement('span')
rootEl.classList.add('__metadock-copy-address-btn__')
rootEl.setAttribute(
'style',
`position:absolute;right:0;display:${
isMobile() ? 'inline-flex' : 'none'
};top: 50%;transform: translateY(-50%)`
)
el?.appendChild(rootEl)
createRoot(rootEl).render(reactNode)
}

const renderTransactionHashPhalconLink = async () => {
const txnTags = document.querySelectorAll<HTMLElement>(
"table a[href^='/tx/']"
)
for (let i = 0; i < txnTags.length; i++) {
const el = txnTags[i]
const href = el.getAttribute('href')
if (!href) continue
const txnHash = href.substring(href.lastIndexOf('/') + 1)
let targetEl: HTMLElement | null = null
if (!el.parentElement) return
targetEl = el.parentElement.parentElement
if (targetEl && txnHash) {
appendIconToElement(targetEl, <PhalconExplorerButton hash={txnHash} />)
}
}
}

export default renderTransactionHashPhalconLink
19 changes: 1 addition & 18 deletions src/content/solanaexpl/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,15 @@
import browser from 'webextension-polyfill'

import '@common/styles/inject.common'
import { getPageName, isHashItemsSimilar } from '@common/utils'
import { URL_UPDATED } from '@common/constants'

import execute from './main'

export const initSolanaExplorer = async () => {
execute()
let pageName = getPageName(window.location.hash.substring(1))
let originURL = window.location.href
browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
if (message === URL_UPDATED) {
const newPageName = getPageName()
if (newPageName !== pageName) {
execute()
} else {
const isSimilar = isHashItemsSimilar(
new URL(originURL).hash,
window.location.hash,
3
)
if (!isSimilar) {
execute()
}
}
originURL = window.location.href
pageName = newPageName
execute()
sendResponse()
}
})
Expand Down
27 changes: 15 additions & 12 deletions src/content/solanaexpl/page-scripts/address.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import $ from 'jquery'
import { store } from '@src/store'

import { lazyLoad } from '../helper'
import { renderFundFlowButton, renderMainAddressLabel } from '../feat-scripts'
import {
renderFundFlowButton,
renderMainAddressLabel,
renderTransactionHashPhalconLink
} from '../feat-scripts'

const createAccountBox = () => {
if ($('#md-account-box').length) return Promise.reject()
Expand All @@ -26,17 +30,16 @@ const createAccountBox = () => {
}

const initAddressPageScript = async () => {
const { fundFlow, enhancedLabels } = await store.get('options')
lazyLoad(
() =>
createAccountBox().then(() => {
if (fundFlow) renderFundFlowButton()
if (enhancedLabels) {
renderMainAddressLabel()
}
}),
`body > div.main-content > div:nth-of-type(3) > div:nth-of-type(2) > div > span[class*="spinner"]`
)
const { fundFlow, enhancedLabels, quick2Parsers } = await store.get('options')
lazyLoad(() => {
createAccountBox().then(() => {
if (fundFlow) renderFundFlowButton()
if (enhancedLabels) {
renderMainAddressLabel()
}
})
if (quick2Parsers) renderTransactionHashPhalconLink()
}, 'span[class*="spinner"]:not(td >*)')
}

export default initAddressPageScript
8 changes: 6 additions & 2 deletions src/content/solanaexpl/page-scripts/tx.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { store } from '@src/store'

import { renderTxPageAddressLabels } from '../feat-scripts'
import {
renderTxPageAddressLabels,
renderAlternativeParsers
} from '../feat-scripts'
import { lazyLoad } from '../helper'

const initTxPageScript = async () => {
const { enhancedLabels } = await store.get('options')
const { enhancedLabels, quick2Parsers } = await store.get('options')
lazyLoad(() => {
if (enhancedLabels) renderTxPageAddressLabels()
if (quick2Parsers) renderAlternativeParsers()
}, `body > div.main-content > div:nth-of-type(3) > div > div > span[class*="spinner"]`)
}

Expand Down
28 changes: 28 additions & 0 deletions src/content/solanafm/components/ParsersButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { type FC } from 'react'

import { PHALCON_EXPLORER_DOMAIN } from '@common/config/uri'
import { getChainSimpleName } from '@common/utils'
import { PHALCON_SUPPORT_LIST } from '@common/constants'
import { IconPhalcon } from '@common/components'

interface Props {
txHash: string
}

const ParsersBtn: FC<Props> = ({ txHash }) => {
const chain = getChainSimpleName()

const pathname = PHALCON_SUPPORT_LIST.find(
item => item.chain === chain
)?.pathname

const handleClick = (e: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
e.preventDefault()
window.open(`${PHALCON_EXPLORER_DOMAIN}/tx/${pathname}/${txHash}`, '_blank')
}

if (!chain) return null
return <IconPhalcon mode="dark" onClick={handleClick} />
}

export default ParsersBtn
Loading

0 comments on commit 5a4c515

Please sign in to comment.