11import "./ContractSourceCode.css" ;
22import { Box , Tabs , Tab , IconButton , useMediaQuery } from "@mui/material" ;
3- import React , { useCallback , useEffect , useState } from "react" ;
3+ import React , { useCallback , useEffect , useMemo , useState } from "react" ;
44import { VerifiedSourceCode } from "./VerifiedSourceCode" ;
55import { DisassembledSourceCode } from "./DisassembledSourceCode" ;
6- import { useLoadContractProof } from "../lib/useLoadContractProof" ;
76import { CenteringBox , IconBox , TitleBox , TitleText } from "./Common.styled" ;
87import verified from "../assets/verified-light.svg" ;
98import { styled } from "@mui/system" ;
@@ -13,12 +12,14 @@ import copy from "../assets/copy.svg";
1312import { downloadSources } from "../lib/downloadSources" ;
1413import useNotification from "../lib/useNotification" ;
1514import { Getters } from "./Getters" ;
16- import { useGetters } from "../lib/getter/useGetters" ;
17-
18- enum CODE {
19- DISASSEMBLED ,
20- SOURCE ,
21- }
15+ import { useLoadVerifierRegistryInfo } from "../lib/useLoadVerifierRegistryInfo" ;
16+ import { useContractAddress } from "../lib/useContractAddress" ;
17+ import { useLoadContractInfo } from "../lib/useLoadContractInfo" ;
18+ import { useQueries } from "@tanstack/react-query" ;
19+ import { loadProofData } from "../lib/useLoadContractProof" ;
20+ import { useIsTestnet } from "./TestnetBar" ;
21+ import { useSyncGetters } from "../lib/getter/useGetters" ;
22+ import { SourcesData } from "@ton-community/contract-verifier-sdk" ;
2223
2324const TitleWrapper = styled ( CenteringBox ) ( {
2425 justifyContent : "space-between" ,
@@ -48,33 +49,122 @@ const SourceCodeTabs = styled(Tabs)({
4849 } ,
4950} ) ;
5051
52+ type ProofData = Partial < SourcesData > & { hasOnchainProof : boolean } ;
53+
54+ type DomIds = {
55+ containerId : string ;
56+ filesId : string ;
57+ contentId : string ;
58+ } ;
59+
60+ type TabConfig =
61+ | { id : string ; label : string ; type : "disassembled" }
62+ | {
63+ id : string ;
64+ label : string ;
65+ type : "sources" ;
66+ proof : ProofData ;
67+ domIds : DomIds ;
68+ getterKey : string ;
69+ }
70+ | {
71+ id : string ;
72+ label : string ;
73+ type : "getters" ;
74+ proof : ProofData ;
75+ getterKey : string ;
76+ } ;
77+
5178function ContractSourceCode ( ) {
52- const { data : contractProof } = useLoadContractProof ( ) ;
53- const [ value , setValue ] = useState < number | undefined > ( undefined ) ;
79+ const { contractAddress } = useContractAddress ( ) ;
80+ const { data : contractInfo } = useLoadContractInfo ( contractAddress ) ;
81+ const { data : verifierRegistry } = useLoadVerifierRegistryInfo ( ) ;
82+ const [ value , setValue ] = useState ( 0 ) ;
5483 const isExtraSmallScreen = useMediaQuery ( "(max-width: 450px)" ) ;
5584 const modifiedCodeBlock = useMediaQuery ( "(max-width: 600px)" ) ;
5685 const { showNotification } = useNotification ( ) ;
86+ const isTestnet = useIsTestnet ( ) ;
5787
58- const handleChange = ( event : React . SyntheticEvent , newValue : number ) => {
59- setValue ( newValue ) ;
60- } ;
88+ const verifierEntries = useMemo ( ( ) => Object . entries ( verifierRegistry ?? { } ) , [ verifierRegistry ] ) ;
89+
90+ const proofQueries = useQueries ( {
91+ queries : verifierEntries . map ( ( [ id , config ] ) => ( {
92+ queryKey : [ "verifierProof" , contractAddress , id , isTestnet ] ,
93+ enabled : ! ! contractAddress && ! ! contractInfo ?. codeCellToCompileBase64 ,
94+ queryFn : ( ) => loadProofData ( contractInfo ! . codeCellToCompileBase64 , config . name , isTestnet ) ,
95+ } ) ) ,
96+ } ) ;
97+
98+ const verifierProofs = useMemo (
99+ ( ) =>
100+ verifierEntries . map ( ( [ id , config ] , index ) => {
101+ const safeKey = `${ contractAddress ?? "unknown" } -${ id } ` . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, "-" ) ;
102+ return {
103+ id,
104+ config,
105+ getterKey : `${ contractAddress ?? "unknown" } ::${ id } ` ,
106+ domIds : {
107+ containerId : `${ safeKey } -container` ,
108+ filesId : `${ safeKey } -files` ,
109+ contentId : `${ safeKey } -content` ,
110+ } ,
111+ proof : proofQueries [ index ] ?. data as ProofData | undefined ,
112+ } ;
113+ } ) ,
114+ [ verifierEntries , proofQueries , contractAddress ] ,
115+ ) ;
61116
62- const onCopy = useCallback ( async ( type : CODE ) => {
63- const element = document . querySelector (
64- type === CODE . SOURCE
65- ? `#myVerifierContent > pre > code > .contract-verifier-code-content`
66- : `pre > code > div.hljs.language-fift` ,
67- ) as HTMLElement ;
68- navigator . clipboard . writeText ( element ?. innerText ) ;
117+ const tabs = useMemo < TabConfig [ ] > ( ( ) => {
118+ const initialTabs : TabConfig [ ] = [
119+ { id : "disassembled" , label : "Disassembled" , type : "disassembled" } ,
120+ ] ;
121+ verifierProofs . forEach ( ( { id, config, proof, getterKey, domIds } ) => {
122+ if ( ! proof ?. hasOnchainProof ) {
123+ return ;
124+ }
125+ const labelSuffix = config . name || id ;
126+ initialTabs . push ( {
127+ id : `sources-${ id } ` ,
128+ label : `Sources (${ labelSuffix } )` ,
129+ type : "sources" ,
130+ proof,
131+ domIds,
132+ getterKey,
133+ } ) ;
134+ initialTabs . push ( {
135+ id : `getters-${ id } ` ,
136+ label : `Getters (${ labelSuffix } )` ,
137+ type : "getters" ,
138+ proof,
139+ getterKey,
140+ } ) ;
141+ } ) ;
142+ return initialTabs ;
143+ } , [ verifierProofs ] ) ;
69144
70- showNotification ( "Copied to clipboard!" , "success" ) ;
71- } , [ ] ) ;
145+ const hasAnyProof = tabs . some ( ( tab ) => tab . type === "sources" ) ;
72146
73147 useEffect ( ( ) => {
74- setValue ( ! ! contractProof ?. hasOnchainProof ? 0 : 1 ) ;
75- } , [ contractProof ?. hasOnchainProof ] ) ;
148+ const firstSourcesIndex = tabs . findIndex ( ( tab ) => tab . type === "sources" ) ;
149+ setValue ( firstSourcesIndex !== - 1 ? firstSourcesIndex : 0 ) ;
150+ } , [ tabs . length , hasAnyProof ] ) ;
76151
77- const { getters } = useGetters ( ) ;
152+ const handleChange = ( event : React . SyntheticEvent , newValue : number ) => {
153+ setValue ( newValue ) ;
154+ } ;
155+
156+ const handleCopy = useCallback (
157+ ( selector : string ) => {
158+ const element = document . querySelector ( selector ) as HTMLElement | null ;
159+ if ( ! element ) return ;
160+ navigator . clipboard . writeText ( element . innerText ) ;
161+ showNotification ( "Copied to clipboard!" , "success" ) ;
162+ } ,
163+ [ showNotification ] ,
164+ ) ;
165+
166+ const activeTab = tabs [ value ] ?? tabs [ 0 ] ;
167+ const activeProof = activeTab && activeTab . type !== "disassembled" ? activeTab . proof : undefined ;
78168
79169 return (
80170 < Box
@@ -91,11 +181,10 @@ function ContractSourceCode() {
91181 < img src = { verified } alt = "Block icon" width = { 41 } height = { 41 } />
92182 </ IconBox >
93183 < TitleText >
94- { ! ! contractProof ?. hasOnchainProof && "Verified" } Source { isExtraSmallScreen && < br /> } { " " }
95- Code
184+ { hasAnyProof && "Verified" } Source { isExtraSmallScreen && < br /> } Code
96185 </ TitleText >
97186 </ CenteringBox >
98- { value === 0 && (
187+ { activeTab ?. type === "sources" && (
99188 < Box
100189 sx = { {
101190 alignSelf : "baseline" ,
@@ -110,7 +199,7 @@ function ContractSourceCode() {
110199 height = { modifiedCodeBlock ? 30 : 37 }
111200 width = { modifiedCodeBlock ? 30 : 167 }
112201 onClick = { ( ) => {
113- contractProof ?. files ?. length && downloadSources ( contractProof . files ) ;
202+ activeProof ?. files ?. length && downloadSources ( activeProof . files ) ;
114203 } } >
115204 < img src = { download } alt = "Download icon" width = { 19 } height = { 19 } />
116205 { modifiedCodeBlock ? "" : "Download sources" }
@@ -121,40 +210,76 @@ function ContractSourceCode() {
121210 </ TitleBox >
122211 < ContentBox p = { 3 } >
123212 < SourceCodeTabs value = { value } onChange = { handleChange } >
124- < Tab
125- sx = { { textTransform : "none" } }
126- disabled = { ! contractProof ?. hasOnchainProof }
127- label = "Sources"
128- />
129- < Tab sx = { { textTransform : "none" } } label = "Disassembled" />
130- < Tab sx = { { textTransform : "none" } } label = { `Getters (${ getters ?. length ?? 0 } )` } />
213+ { tabs . map ( ( tab , index ) => (
214+ < Tab key = { tab . id } value = { index } sx = { { textTransform : "none" } } label = { tab . label } />
215+ ) ) }
131216 </ SourceCodeTabs >
132- < Box sx = { { display : value === 0 ? "block" : "none" } } >
133- < VerifiedSourceCode button = { < CopyButton onCopy = { onCopy } copyText = { CODE . SOURCE } /> } />
134- </ Box >
135- < Box sx = { { display : value === 1 ? "block" : "none" } } >
136- < DisassembledSourceCode
137- button = { < CopyButton onCopy = { onCopy } copyText = { CODE . DISASSEMBLED } /> }
138- />
139- </ Box >
140- < Box sx = { { display : value === 2 ? "block" : "none" } } >
141- < Getters />
142- </ Box >
217+ { tabs . map ( ( tab , index ) => {
218+ if ( tab . type === "disassembled" ) {
219+ return (
220+ < Box key = { tab . id } sx = { { display : value === index ? "block" : "none" } } >
221+ < DisassembledSourceCode
222+ button = {
223+ < CopyButton onCopy = { ( ) => handleCopy ( `pre > code > div.hljs.language-fift` ) } />
224+ }
225+ />
226+ </ Box >
227+ ) ;
228+ }
229+ if ( tab . type === "sources" ) {
230+ return (
231+ < Box key = { tab . id } sx = { { display : value === index ? "block" : "none" } } >
232+ < VerifiedSourceCode
233+ proofData = { tab . proof }
234+ domIds = { tab . domIds ! }
235+ button = {
236+ < CopyButton
237+ onCopy = { ( ) =>
238+ handleCopy (
239+ `#${ tab . domIds ! . contentId } > pre > code > .contract-verifier-code-content` ,
240+ )
241+ }
242+ />
243+ }
244+ />
245+ </ Box >
246+ ) ;
247+ }
248+ return (
249+ < VerifierGettersPanel
250+ key = { tab . id }
251+ getterKey = { tab . getterKey }
252+ proof = { tab . proof }
253+ isVisible = { value === index }
254+ />
255+ ) ;
256+ } ) }
143257 </ ContentBox >
144258 </ Box >
145259 ) ;
146260}
147261
148- const CopyButton = ( {
149- copyText,
150- onCopy,
262+ function VerifierGettersPanel ( {
263+ getterKey,
264+ proof,
265+ isVisible,
151266} : {
152- copyText : CODE ;
153- onCopy : ( type : CODE ) => Promise < void > ;
154- } ) => {
267+ getterKey : string ;
268+ proof : ProofData ;
269+ isVisible : boolean ;
270+ } ) {
271+ useSyncGetters ( getterKey , proof ?. files ) ;
272+ return (
273+ < Box sx = { { display : isVisible ? "block" : "none" } } >
274+ < Getters getterKey = { getterKey } />
275+ </ Box >
276+ ) ;
277+ }
278+
279+ const CopyButton = ( { onCopy } : { onCopy : ( ) => void } ) => {
155280 return (
156281 < CopyBox >
157- < IconButton onClick = { ( ) => onCopy ( copyText ) } >
282+ < IconButton onClick = { onCopy } >
158283 < img alt = "Copy Icon" src = { copy } width = { 16 } height = { 16 } />
159284 </ IconButton >
160285 </ CopyBox >
0 commit comments