Commit 9edd7b47 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Contract tab: finding matching bytecode states (#2680)

* Contract tab: finding matching bytecode states

Fixes #2678

* handle case when there is only one sub-tab on contract tab

* tests

* prevent calling the smart contract resource twice when opening the tab

* [wip] add debug logs

* change api host for op sepolia

* rollback debug and update screenshots
parent 01253ce6
...@@ -35,7 +35,9 @@ SocketMessage.AddressTxsPending | ...@@ -35,7 +35,9 @@ SocketMessage.AddressTxsPending |
SocketMessage.AddressTokenTransfer | SocketMessage.AddressTokenTransfer |
SocketMessage.AddressChangedBytecode | SocketMessage.AddressChangedBytecode |
SocketMessage.AddressFetchedBytecode | SocketMessage.AddressFetchedBytecode |
SocketMessage.EthBytecodeDbLookupStarted |
SocketMessage.SmartContractWasVerified | SocketMessage.SmartContractWasVerified |
SocketMessage.SmartContractWasNotVerified |
SocketMessage.TokenTransfers | SocketMessage.TokenTransfers |
SocketMessage.TokenTotalSupply | SocketMessage.TokenTotalSupply |
SocketMessage.TokenInstanceMetadataFetched | SocketMessage.TokenInstanceMetadataFetched |
...@@ -76,7 +78,9 @@ export namespace SocketMessage { ...@@ -76,7 +78,9 @@ export namespace SocketMessage {
export type AddressTokenTransfer = SocketMessageParamsGeneric<'token_transfer', { token_transfers: Array<TokenTransfer> }>; export type AddressTokenTransfer = SocketMessageParamsGeneric<'token_transfer', { token_transfers: Array<TokenTransfer> }>;
export type AddressChangedBytecode = SocketMessageParamsGeneric<'changed_bytecode', Record<string, never>>; export type AddressChangedBytecode = SocketMessageParamsGeneric<'changed_bytecode', Record<string, never>>;
export type AddressFetchedBytecode = SocketMessageParamsGeneric<'fetched_bytecode', { fetched_bytecode: string }>; export type AddressFetchedBytecode = SocketMessageParamsGeneric<'fetched_bytecode', { fetched_bytecode: string }>;
export type EthBytecodeDbLookupStarted = SocketMessageParamsGeneric<'eth_bytecode_db_lookup_started', Record<string, never>>;
export type SmartContractWasVerified = SocketMessageParamsGeneric<'smart_contract_was_verified', Record<string, never>>; export type SmartContractWasVerified = SocketMessageParamsGeneric<'smart_contract_was_verified', Record<string, never>>;
export type SmartContractWasNotVerified = SocketMessageParamsGeneric<'smart_contract_was_not_verified', Record<string, never>>;
export type TokenTransfers = SocketMessageParamsGeneric<'token_transfer', { token_transfer: number }>; export type TokenTransfers = SocketMessageParamsGeneric<'token_transfer', { token_transfer: number }>;
export type TokenTotalSupply = SocketMessageParamsGeneric<'total_supply', { total_supply: number }>; export type TokenTotalSupply = SocketMessageParamsGeneric<'total_supply', { total_supply: number }>;
export type TokenInstanceMetadataFetched = SocketMessageParamsGeneric<'fetched_token_instance_metadata', TokenInstanceMetadataSocketMessage>; export type TokenInstanceMetadataFetched = SocketMessageParamsGeneric<'fetched_token_instance_metadata', TokenInstanceMetadataSocketMessage>;
......
...@@ -73,7 +73,9 @@ export function sendMessage(socket: WebSocket, channel: Channel, msg: 'verificat ...@@ -73,7 +73,9 @@ export function sendMessage(socket: WebSocket, channel: Channel, msg: 'verificat
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'total_supply', payload: { total_supply: number }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'total_supply', payload: { total_supply: number }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'changed_bytecode', payload: Record<string, never>): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'changed_bytecode', payload: Record<string, never>): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'fetched_bytecode', payload: { fetched_bytecode: string }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'fetched_bytecode', payload: { fetched_bytecode: string }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'eth_bytecode_db_lookup_started', payload: Record<string, never>): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'smart_contract_was_verified', payload: Record<string, never>): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'smart_contract_was_verified', payload: Record<string, never>): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'smart_contract_was_not_verified', payload: Record<string, never>): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'token_transfer', payload: { token_transfers: Array<TokenTransfer> }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'token_transfer', payload: { token_transfers: Array<TokenTransfer> }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'fetched_token_instance_metadata', payload: TokenInstanceMetadataSocketMessage): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'fetched_token_instance_metadata', payload: TokenInstanceMetadataSocketMessage): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: string, payload: unknown): void { export function sendMessage(socket: WebSocket, channel: Channel, msg: string, payload: unknown): void {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 268 184" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 268 184" fill="none">
<path d="M11.856.536a4 4 0 0 1 4 0l9.857 5.69a4 4 0 0 1 2 3.465v11.38a4 4 0 0 1-2 3.465l-9.857 5.69a4 4 0 0 1-4 0L2 24.537a4 4 0 0 1-2-3.464V9.69a4 4 0 0 1 2-3.464L11.856.536Z" fill="url(#a)"/> <path d="M11.856.536a4 4 0 0 1 4 0l9.857 5.69a4 4 0 0 1 2 3.465v11.38a4 4 0 0 1-2 3.465l-9.857 5.69a4 4 0 0 1-4 0L2 24.537a4 4 0 0 1-2-3.464V9.69a4 4 0 0 1 2-3.464l9.856-5.69Z" fill="url(#a)"/>
<path d="M55.856 21.536a4 4 0 0 0-4 0L42 27.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V30.69a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#b)"/> <path d="M55.856 21.536a4 4 0 0 0-4 0L42 27.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V30.69a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#b)"/>
<path d="M55.856 65.536a4 4 0 0 0-4 0L42 71.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V74.69a4 4 0 0 0-2-3.465l-9.857-5.69Z" fill="url(#c)"/> <path d="M55.856 65.536a4 4 0 0 0-4 0L42 71.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V74.69a4 4 0 0 0-2-3.465l-9.857-5.69Z" fill="url(#c)"/>
<path d="M51.856 109.536a3.998 3.998 0 0 1 4 0l9.857 5.691a4 4 0 0 1 2 3.464v11.381a4 4 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L42 133.536a4 4 0 0 1-2-3.464v-11.381a4 4 0 0 1 2-3.464l9.856-5.691Z" fill="url(#d)"/> <path d="M51.856 109.536a3.998 3.998 0 0 1 4 0l9.857 5.691a4 4 0 0 1 2 3.464v11.381a4 4 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L42 133.536a4 4 0 0 1-2-3.464v-11.381a4 4 0 0 1 2-3.464l9.856-5.691Z" fill="url(#d)"/>
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<path d="M15.856 44.536a4 4 0 0 0-4 0L2 50.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V53.69a4 4 0 0 0-2-3.465l-9.857-5.69Z" fill="url(#f)"/> <path d="M15.856 44.536a4 4 0 0 0-4 0L2 50.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V53.69a4 4 0 0 0-2-3.465l-9.857-5.69Z" fill="url(#f)"/>
<path d="M11.856 88.536a4 4 0 0 1 4 0l9.857 5.69a4 4 0 0 1 2 3.465v11.381a4 4 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L2 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#g)"/> <path d="M11.856 88.536a4 4 0 0 1 4 0l9.857 5.69a4 4 0 0 1 2 3.465v11.381a4 4 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L2 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#g)"/>
<path d="M15.856 132.536a3.998 3.998 0 0 0-4 0L2 138.227a4 4 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4 4 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#h)"/> <path d="M15.856 132.536a3.998 3.998 0 0 0-4 0L2 138.227a4 4 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4 4 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#h)"/>
<path d="M91.856.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.38c0 1.43-.763 2.75-2 3.465l-9.857 5.69a4 4 0 0 1-4 0L82 24.537a4 4 0 0 1-2-3.464V9.69a4 4 0 0 1 2-3.464L91.856.536Z" fill="url(#i)"/> <path d="M91.856.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.38c0 1.43-.763 2.75-2 3.465l-9.857 5.69a4 4 0 0 1-4 0L82 24.537a4 4 0 0 1-2-3.464V9.69a4 4 0 0 1 2-3.464l9.856-5.69Z" fill="url(#i)"/>
<path d="M95.856 44.536a4 4 0 0 0-4 0L82 50.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4.001 4.001 0 0 0 2-3.464V53.69c0-1.43-.763-2.75-2-3.465l-9.857-5.69Z" fill="url(#j)"/> <path d="M95.856 44.536a4 4 0 0 0-4 0L82 50.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4.001 4.001 0 0 0 2-3.464V53.69c0-1.43-.763-2.75-2-3.465l-9.857-5.69Z" fill="url(#j)"/>
<path d="M91.856 88.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.381a4.002 4.002 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L82 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#k)"/> <path d="M91.856 88.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.381a4.002 4.002 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L82 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#k)"/>
<path d="M95.856 132.536a3.998 3.998 0 0 0-4 0L82 138.227a4 4 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4.002 4.002 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#l)"/> <path d="M95.856 132.536a3.998 3.998 0 0 0-4 0L82 138.227a4 4 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4.002 4.002 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#l)"/>
...@@ -28,115 +28,115 @@ ...@@ -28,115 +28,115 @@
<path d="M251.856 88.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.381a4.002 4.002 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L242 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#A)"/> <path d="M251.856 88.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.381a4.002 4.002 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L242 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#A)"/>
<path d="M255.856 132.536a3.998 3.998 0 0 0-4 0L242 138.227a3.998 3.998 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4.002 4.002 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#B)"/> <path d="M255.856 132.536a3.998 3.998 0 0 0-4 0L242 138.227a3.998 3.998 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4.002 4.002 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#B)"/>
<defs> <defs>
<radialGradient id="a" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="a" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="d" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="d" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="e" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="e" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="f" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="f" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="g" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="g" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="h" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="h" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="i" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="i" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="j" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="j" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="k" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="k" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="l" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="l" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="m" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="m" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="n" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="n" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="o" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="o" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="p" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="p" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="q" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="q" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="r" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="r" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="s" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="s" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="t" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="t" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="u" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="u" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="v" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="v" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="w" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="w" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="x" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="x" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="y" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="y" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="z" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="z" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="A" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="A" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
<radialGradient id="B" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="B" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#FFDC94"/> <stop stop-color="#FFDC94"/>
<stop offset="1" stop-color="#FFEFCE"/> <stop offset="1" stop-color="#FFEFCE"/>
</radialGradient> </radialGradient>
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 268 184" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 268 184" fill="none">
<path d="M11.856.536a4 4 0 0 1 4 0l9.857 5.69a4 4 0 0 1 2 3.465v11.38a4 4 0 0 1-2 3.465l-9.857 5.69a4 4 0 0 1-4 0L2 24.537a4 4 0 0 1-2-3.464V9.69a4 4 0 0 1 2-3.464L11.856.536Z" fill="url(#a)"/> <path d="M11.856.536a4 4 0 0 1 4 0l9.857 5.69a4 4 0 0 1 2 3.465v11.38a4 4 0 0 1-2 3.465l-9.857 5.69a4 4 0 0 1-4 0L2 24.537a4 4 0 0 1-2-3.464V9.69a4 4 0 0 1 2-3.464l9.856-5.69Z" fill="url(#a)"/>
<path d="M55.856 21.536a4 4 0 0 0-4 0L42 27.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V30.69a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#b)"/> <path d="M55.856 21.536a4 4 0 0 0-4 0L42 27.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V30.69a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#b)"/>
<path d="M55.856 65.536a4 4 0 0 0-4 0L42 71.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V74.69a4 4 0 0 0-2-3.465l-9.857-5.69Z" fill="url(#c)"/> <path d="M55.856 65.536a4 4 0 0 0-4 0L42 71.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V74.69a4 4 0 0 0-2-3.465l-9.857-5.69Z" fill="url(#c)"/>
<path d="M51.856 109.536a3.998 3.998 0 0 1 4 0l9.857 5.691a4 4 0 0 1 2 3.464v11.381a4 4 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L42 133.536a4 4 0 0 1-2-3.464v-11.381a4 4 0 0 1 2-3.464l9.856-5.691Z" fill="url(#d)"/> <path d="M51.856 109.536a3.998 3.998 0 0 1 4 0l9.857 5.691a4 4 0 0 1 2 3.464v11.381a4 4 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L42 133.536a4 4 0 0 1-2-3.464v-11.381a4 4 0 0 1 2-3.464l9.856-5.691Z" fill="url(#d)"/>
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<path d="M15.856 44.536a4 4 0 0 0-4 0L2 50.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V53.69a4 4 0 0 0-2-3.465l-9.857-5.69Z" fill="url(#f)"/> <path d="M15.856 44.536a4 4 0 0 0-4 0L2 50.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4 4 0 0 0 2-3.464V53.69a4 4 0 0 0-2-3.465l-9.857-5.69Z" fill="url(#f)"/>
<path d="M11.856 88.536a4 4 0 0 1 4 0l9.857 5.69a4 4 0 0 1 2 3.465v11.381a4 4 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L2 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#g)"/> <path d="M11.856 88.536a4 4 0 0 1 4 0l9.857 5.69a4 4 0 0 1 2 3.465v11.381a4 4 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L2 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#g)"/>
<path d="M15.856 132.536a3.998 3.998 0 0 0-4 0L2 138.227a4 4 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4 4 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#h)"/> <path d="M15.856 132.536a3.998 3.998 0 0 0-4 0L2 138.227a4 4 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4 4 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#h)"/>
<path d="M91.856.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.38c0 1.43-.763 2.75-2 3.465l-9.857 5.69a4 4 0 0 1-4 0L82 24.537a4 4 0 0 1-2-3.464V9.69a4 4 0 0 1 2-3.464L91.856.536Z" fill="url(#i)"/> <path d="M91.856.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.38c0 1.43-.763 2.75-2 3.465l-9.857 5.69a4 4 0 0 1-4 0L82 24.537a4 4 0 0 1-2-3.464V9.69a4 4 0 0 1 2-3.464l9.856-5.69Z" fill="url(#i)"/>
<path d="M95.856 44.536a4 4 0 0 0-4 0L82 50.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4.001 4.001 0 0 0 2-3.464V53.69c0-1.43-.763-2.75-2-3.465l-9.857-5.69Z" fill="url(#j)"/> <path d="M95.856 44.536a4 4 0 0 0-4 0L82 50.226a4 4 0 0 0-2 3.465v11.38a4 4 0 0 0 2 3.465l9.856 5.69a4 4 0 0 0 4 0l9.857-5.69a4.001 4.001 0 0 0 2-3.464V53.69c0-1.43-.763-2.75-2-3.465l-9.857-5.69Z" fill="url(#j)"/>
<path d="M91.856 88.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.381a4.002 4.002 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L82 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#k)"/> <path d="M91.856 88.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.381a4.002 4.002 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L82 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#k)"/>
<path d="M95.856 132.536a3.998 3.998 0 0 0-4 0L82 138.227a4 4 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4.002 4.002 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#l)"/> <path d="M95.856 132.536a3.998 3.998 0 0 0-4 0L82 138.227a4 4 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4.002 4.002 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#l)"/>
...@@ -28,115 +28,115 @@ ...@@ -28,115 +28,115 @@
<path d="M251.856 88.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.381a4.002 4.002 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L242 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#A)"/> <path d="M251.856 88.536a4 4 0 0 1 4 0l9.857 5.69c1.237.715 2 2.036 2 3.465v11.381a4.002 4.002 0 0 1-2 3.464l-9.857 5.691a4.003 4.003 0 0 1-4 0L242 112.536a4 4 0 0 1-2-3.464V97.691a4 4 0 0 1 2-3.465l9.856-5.69Z" fill="url(#A)"/>
<path d="M255.856 132.536a3.998 3.998 0 0 0-4 0L242 138.227a3.998 3.998 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4.002 4.002 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#B)"/> <path d="M255.856 132.536a3.998 3.998 0 0 0-4 0L242 138.227a3.998 3.998 0 0 0-2 3.464v11.381a4 4 0 0 0 2 3.464l9.856 5.691a4.003 4.003 0 0 0 4 0l9.857-5.691a4.002 4.002 0 0 0 2-3.464v-11.381a4 4 0 0 0-2-3.464l-9.857-5.691Z" fill="url(#B)"/>
<defs> <defs>
<radialGradient id="a" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="a" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="d" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="d" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="e" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="e" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="f" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="f" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="g" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="g" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="h" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="h" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="i" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="i" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="j" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="j" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="k" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="k" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="l" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="l" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="m" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="m" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="n" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="n" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="o" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="o" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="p" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="p" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="q" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="q" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="r" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="r" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="s" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="s" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="t" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="t" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="u" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="u" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="v" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="v" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="w" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="w" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="x" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="x" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="y" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="y" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="z" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="z" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="A" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="A" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
<radialGradient id="B" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 112.553 -148.453 0 129.985 91.882)"> <radialGradient id="B" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="scale(148.453 112.553) rotate(90 .03 .846)">
<stop stop-color="#C37300"/> <stop stop-color="#C37300"/>
<stop offset="1" stop-color="#E1910E"/> <stop offset="1" stop-color="#E1910E"/>
</radialGradient> </radialGradient>
......
...@@ -42,10 +42,6 @@ const AdaptiveTabs = (props: Props) => { ...@@ -42,10 +42,6 @@ const AdaptiveTabs = (props: Props) => {
} }
}, [ defaultValue ]); }, [ defaultValue ]);
if (tabs.length === 1) {
return <div>{ tabs[0].component }</div>;
}
return ( return (
<TabsRoot <TabsRoot
position="relative" position="relative"
......
...@@ -86,6 +86,10 @@ const AdaptiveTabsList = (props: Props) => { ...@@ -86,6 +86,10 @@ const AdaptiveTabsList = (props: Props) => {
const activeTabIndex = tabsList.findIndex((tab) => getTabValue(tab) === activeTab) ?? 0; const activeTabIndex = tabsList.findIndex((tab) => getTabValue(tab) === activeTab) ?? 0;
useScrollToActiveTab({ activeTabIndex, listRef, tabsRefs, isMobile, isLoading }); useScrollToActiveTab({ activeTabIndex, listRef, tabsRefs, isMobile, isLoading });
if (tabs.length === 1 && !leftSlot && !rightSlot) {
return null;
}
const isReady = !isLoading && tabsCut !== undefined; const isReady = !isLoading && tabsCut !== undefined;
return ( return (
...@@ -133,7 +137,7 @@ const AdaptiveTabsList = (props: Props) => { ...@@ -133,7 +137,7 @@ const AdaptiveTabsList = (props: Props) => {
</Box> </Box>
) )
} }
{ tabsList.map((tab, index) => { { tabs.length > 1 && tabsList.map((tab, index) => {
const value = getTabValue(tab); const value = getTabValue(tab);
const ref = tabsRefs[index]; const ref = tabsRefs[index];
......
...@@ -7,27 +7,27 @@ import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; ...@@ -7,27 +7,27 @@ import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import * as socketServer from 'playwright/fixtures/socketServer'; import * as socketServer from 'playwright/fixtures/socketServer';
import { test, expect } from 'playwright/lib'; import { test, expect } from 'playwright/lib';
import AddressContract from './AddressContract.pwstory'; import AddressContract from './AddressContract';
const hash = addressMock.contract.hash; const hash = addressMock.contract.hash;
test.beforeEach(async({ mockApiResponse }) => { test.describe('ABI functionality', () => {
test.beforeEach(async({ mockApiResponse }) => {
await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash } }); await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash } });
await mockApiResponse( await mockApiResponse(
'general:contract', 'general:contract',
{ ...contractInfoMock.verified, abi: [ ...contractMethodsMock.read, ...contractMethodsMock.write ] }, { ...contractInfoMock.verified, abi: [ ...contractMethodsMock.read, ...contractMethodsMock.write ] },
{ pathParams: { hash } }, { pathParams: { hash } },
); );
}); });
test.describe('ABI functionality', () => {
test('read', async({ render, createSocket }) => { test('read', async({ render, createSocket }) => {
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { hash, tab: 'read_contract' }, query: { hash, tab: 'read_contract' },
}, },
}; };
const component = await render(<AddressContract/>, { hooksConfig }, { withSocket: true }); const component = await render(<AddressContract addressData={ addressMock.contract }/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket(); const socket = await createSocket();
await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase()); await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase());
...@@ -43,7 +43,7 @@ test.describe('ABI functionality', () => { ...@@ -43,7 +43,7 @@ test.describe('ABI functionality', () => {
}, },
}; };
await mockEnvs(ENVS_MAP.noWalletClient); await mockEnvs(ENVS_MAP.noWalletClient);
const component = await render(<AddressContract/>, { hooksConfig }, { withSocket: true }); const component = await render(<AddressContract addressData={ addressMock.contract }/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket(); const socket = await createSocket();
await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase()); await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase());
...@@ -58,7 +58,7 @@ test.describe('ABI functionality', () => { ...@@ -58,7 +58,7 @@ test.describe('ABI functionality', () => {
query: { hash, tab: 'write_contract' }, query: { hash, tab: 'write_contract' },
}, },
}; };
const component = await render(<AddressContract/>, { hooksConfig }, { withSocket: true }); const component = await render(<AddressContract addressData={ addressMock.contract }/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket(); const socket = await createSocket();
await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase()); await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase());
...@@ -80,7 +80,7 @@ test.describe('ABI functionality', () => { ...@@ -80,7 +80,7 @@ test.describe('ABI functionality', () => {
}; };
await mockEnvs(ENVS_MAP.noWalletClient); await mockEnvs(ENVS_MAP.noWalletClient);
const component = await render(<AddressContract/>, { hooksConfig }, { withSocket: true }); const component = await render(<AddressContract addressData={ addressMock.contract }/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket(); const socket = await createSocket();
await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase()); await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase());
...@@ -94,3 +94,65 @@ test.describe('ABI functionality', () => { ...@@ -94,3 +94,65 @@ test.describe('ABI functionality', () => {
await expect(component.getByLabel('5.').getByRole('button', { name: 'Write' })).toBeDisabled(); await expect(component.getByLabel('5.').getByRole('button', { name: 'Write' })).toBeDisabled();
}); });
}); });
test.describe('auto verification status', () => {
const addressData = { ...addressMock.contract, is_verified: false, implementations: [] };
let contractApiUrl: string;
test.beforeEach(async({ mockApiResponse }) => {
await mockApiResponse('general:address', addressData, { pathParams: { hash } });
contractApiUrl = await mockApiResponse('general:contract', contractInfoMock.nonVerified, { pathParams: { hash } });
});
test('base flow', async({ render, createSocket }) => {
const hooksConfig = {
router: {
query: { hash, tab: 'contract' },
},
};
const component = await render(<AddressContract addressData={ addressData }/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'addresses:' + addressData.hash.toLowerCase());
socketServer.sendMessage(socket, channel, 'eth_bytecode_db_lookup_started', { });
const tabs = component.getByRole('tablist').first();
await expect(tabs).toHaveScreenshot();
});
test('after verification will refetch contract data', async({ page, render, createSocket }) => {
const hooksConfig = {
router: {
query: { hash, tab: 'contract' },
},
};
await render(<AddressContract addressData={ addressData }/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'addresses:' + addressData.hash.toLowerCase());
socketServer.sendMessage(socket, channel, 'smart_contract_was_verified', { });
const contractRequest = await page.waitForRequest(contractApiUrl);
expect(contractRequest).toBeTruthy();
});
test('with one tab', async({ render, createSocket, mockEnvs }) => {
const hooksConfig = {
router: {
query: { hash, tab: 'contract' },
},
};
await mockEnvs([
[ 'NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED', 'false' ],
]);
const component = await render(<AddressContract addressData={ addressData }/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'addresses:' + addressData.hash.toLowerCase());
socketServer.sendMessage(socket, channel, 'smart_contract_was_not_verified', { });
const tabs = component.getByRole('tablist').first();
await expect(tabs).toHaveScreenshot();
});
});
import { useRouter } from 'next/router';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString';
import useContractTabs from 'ui/address/contract/useContractTabs';
import AddressContract from './AddressContract';
const AddressContractPwStory = () => {
const router = useRouter();
const hash = getQueryParamString(router.query.hash);
const addressQuery = useApiQuery('general:address', { pathParams: { hash } });
const { tabs } = useContractTabs(addressQuery.data, false);
return <AddressContract tabs={ tabs } shouldRender={ true } isLoading={ false }/>;
};
export default AddressContractPwStory;
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; import type { SocketMessage } from 'lib/socket/types';
import type { Address } from 'types/api/address';
import { getResourceKey } from 'lib/api/useApiQuery';
import delay from 'lib/delay';
import useIsMobile from 'lib/hooks/useIsMobile';
import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import { SECOND } from 'toolkit/utils/consts';
import type { TContractAutoVerificationStatus } from './contract/ContractAutoVerificationStatus';
import ContractAutoVerificationStatus from './contract/ContractAutoVerificationStatus';
import useContractTabs from './contract/useContractTabs';
import { CONTRACT_TAB_IDS } from './contract/utils';
interface Props { interface Props {
tabs: Array<TabItemRegular>; addressData: Address | undefined;
isLoading: boolean; isLoading?: boolean;
shouldRender?: boolean; hasMudTab?: boolean;
} }
const AddressContract = ({ tabs, isLoading, shouldRender }: Props) => { const AddressContract = ({ addressData, isLoading = false, hasMudTab }: Props) => {
if (!shouldRender) { const [ isQueryEnabled, setIsQueryEnabled ] = React.useState(false);
const [ autoVerificationStatus, setAutoVerificationStatus ] = React.useState<TContractAutoVerificationStatus | null>(null);
const router = useRouter();
const queryClient = useQueryClient();
const isMobile = useIsMobile();
const handleChannelJoin = React.useCallback(() => {
setIsQueryEnabled(true);
}, []);
const handleChannelError = React.useCallback(() => {
setIsQueryEnabled(true);
}, []);
const tab = getQueryParamString(router.query.tab);
const isSocketEnabled = Boolean(addressData?.hash) && addressData?.is_contract && !isLoading && CONTRACT_TAB_IDS.concat('contract' as never).includes(tab);
const channel = useSocketChannel({
topic: `addresses:${ addressData?.hash?.toLowerCase() }`,
isDisabled: !isSocketEnabled,
onJoin: handleChannelJoin,
onSocketError: handleChannelError,
});
const contractTabs = useContractTabs({
addressData,
isEnabled: isQueryEnabled,
hasMudTab,
channel,
});
const handleLookupStartedMessage: SocketMessage.EthBytecodeDbLookupStarted['handler'] = React.useCallback(() => {
setAutoVerificationStatus('pending');
}, []);
const handleContractWasVerifiedMessage: SocketMessage.SmartContractWasVerified['handler'] = React.useCallback(async() => {
setAutoVerificationStatus('success');
await queryClient.refetchQueries({
queryKey: getResourceKey('general:address', { pathParams: { hash: addressData?.hash } }),
});
await queryClient.refetchQueries({
queryKey: getResourceKey('general:contract', { pathParams: { hash: addressData?.hash } }),
});
setAutoVerificationStatus(null);
}, [ addressData?.hash, queryClient ]);
const handleContractWasNotVerifiedMessage: SocketMessage.SmartContractWasNotVerified['handler'] = React.useCallback(async() => {
setAutoVerificationStatus('failed');
await delay(10 * SECOND);
setAutoVerificationStatus(null);
}, []);
useSocketMessage({ channel, event: 'eth_bytecode_db_lookup_started', handler: handleLookupStartedMessage });
useSocketMessage({ channel, event: 'smart_contract_was_verified', handler: handleContractWasVerifiedMessage });
useSocketMessage({ channel, event: 'smart_contract_was_not_verified', handler: handleContractWasNotVerifiedMessage });
if (isLoading) {
return null; return null;
} }
const rightSlot = autoVerificationStatus ?
<ContractAutoVerificationStatus status={ autoVerificationStatus } mode={ isMobile && contractTabs.tabs.length > 1 ? 'tooltip' : 'inline' }/> :
null;
return ( return (
<RoutedTabs tabs={ tabs } variant="secondary" size="sm" isLoading={ isLoading }/> <RoutedTabs
tabs={ contractTabs.tabs }
variant="secondary"
size="sm"
isLoading={ contractTabs.isLoading }
rightSlot={ rightSlot }
rightSlotProps={{ ml: contractTabs.tabs.length > 1 ? { base: 'auto', md: 6 } : 0 }}
/>
); );
}; };
......
import { Box, HStack, Spinner } from '@chakra-ui/react';
import React from 'react';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
const STATUS_MAP = {
pending: {
text: 'Checking contract verification',
leftElement: <Spinner size="sm"/>,
},
success: {
text: 'Contract successfully verified',
leftElement: <IconSvg name="verified_slim" boxSize={ 5 } color="green.500"/>,
},
failed: {
text: 'Contract not verified automatically. Please verify manually.',
leftElement: <IconSvg name="status/warning" boxSize={ 5 } color="orange.400"/>,
},
};
export type TContractAutoVerificationStatus = keyof typeof STATUS_MAP;
interface Props {
status: TContractAutoVerificationStatus;
mode?: 'inline' | 'tooltip';
}
const ContractAutoVerificationStatus = ({ status, mode = 'inline' }: Props) => {
return (
<Tooltip content={ STATUS_MAP[status].text } disabled={ mode === 'inline' }>
<HStack gap={ 2 } whiteSpace="pre-wrap">
{ STATUS_MAP[status].leftElement }
<Box display={ mode === 'inline' ? 'inline' : 'none' } textStyle="sm">{ STATUS_MAP[status].text }</Box>
</HStack>
</Tooltip>
);
};
export default React.memo(ContractAutoVerificationStatus);
...@@ -18,13 +18,11 @@ const hooksConfig = { ...@@ -18,13 +18,11 @@ const hooksConfig = {
// test cases which use socket cannot run in parallel since the socket server always run on the same port // test cases which use socket cannot run in parallel since the socket server always run on the same port
test.describe.configure({ mode: 'serial' }); test.describe.configure({ mode: 'serial' });
let addressApiUrl: string;
test.beforeEach(async({ mockApiResponse, page }) => { test.beforeEach(async({ mockApiResponse, page }) => {
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/**', (route) => { await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/**', (route) => {
route.abort(); route.abort();
}); });
addressApiUrl = await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash: addressMock.contract.hash } }); await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash: addressMock.contract.hash } });
}); });
test.describe('full view', () => { test.describe('full view', () => {
...@@ -103,19 +101,6 @@ test.describe('mobile view', () => { ...@@ -103,19 +101,6 @@ test.describe('mobile view', () => {
}); });
}); });
test('verified via lookup in eth_bytecode_db', async({ render, mockApiResponse, createSocket, page }) => {
const contractApiUrl = await mockApiResponse('general:contract', contractMock.nonVerified, { pathParams: { hash: addressMock.contract.hash } });
await render(<ContractDetails/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
await page.waitForResponse(contractApiUrl);
socketServer.sendMessage(socket, channel, 'smart_contract_was_verified', {});
const request = await page.waitForRequest(addressApiUrl);
expect(request).toBeTruthy();
});
test('verified with multiple sources', async({ render, page, mockApiResponse, createSocket }) => { test('verified with multiple sources', async({ render, page, mockApiResponse, createSocket }) => {
await mockApiResponse('general:contract', contractMock.withMultiplePaths, { pathParams: { hash: addressMock.contract.hash } }); await mockApiResponse('general:contract', contractMock.withMultiplePaths, { pathParams: { hash: addressMock.contract.hash } });
await render(<ContractDetails/>, { hooksConfig }, { withSocket: true }); await render(<ContractDetails/>, { hooksConfig }, { withSocket: true });
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import type { Channel } from 'phoenix'; import type { Channel } from 'phoenix';
import React from 'react'; import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { Address } from 'types/api/address'; import type { Address } from 'types/api/address';
import type { AddressImplementation } from 'types/api/addressParams'; import type { AddressImplementation } from 'types/api/addressParams';
import type { SmartContract } from 'types/api/contract'; import type { SmartContract } from 'types/api/contract';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketMessage from 'lib/socket/useSocketMessage';
import * as stubs from 'stubs/contract'; import * as stubs from 'stubs/contract';
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
...@@ -36,8 +33,6 @@ const ContractDetails = ({ addressData, channel, mainContractQuery }: Props) => ...@@ -36,8 +33,6 @@ const ContractDetails = ({ addressData, channel, mainContractQuery }: Props) =>
const router = useRouter(); const router = useRouter();
const sourceAddress = getQueryParamString(router.query.source_address); const sourceAddress = getQueryParamString(router.query.source_address);
const queryClient = useQueryClient();
const sourceItems: Array<AddressImplementation> = React.useMemo(() => { const sourceItems: Array<AddressImplementation> = React.useMemo(() => {
const currentAddressDefaultName = addressData?.proxy_type === 'eip7702' ? 'Current address' : 'Current contract'; const currentAddressDefaultName = addressData?.proxy_type === 'eip7702' ? 'Current address' : 'Current contract';
const currentAddressItem = { address_hash: addressData.hash, name: addressData?.name || currentAddressDefaultName }; const currentAddressItem = { address_hash: addressData.hash, name: addressData?.name || currentAddressDefaultName };
...@@ -56,30 +51,15 @@ const ContractDetails = ({ addressData, channel, mainContractQuery }: Props) => ...@@ -56,30 +51,15 @@ const ContractDetails = ({ addressData, channel, mainContractQuery }: Props) =>
const contractQuery = useApiQuery('general:contract', { const contractQuery = useApiQuery('general:contract', {
pathParams: { hash: selectedItem?.address_hash }, pathParams: { hash: selectedItem?.address_hash },
queryOptions: { queryOptions: {
enabled: Boolean(selectedItem?.address_hash && !mainContractQuery.isPlaceholderData), enabled: Boolean(selectedItem?.address_hash && !mainContractQuery.isPlaceholderData && selectedItem.address_hash !== addressData.hash),
refetchOnMount: false, refetchOnMount: false,
placeholderData: addressData?.is_verified ? stubs.CONTRACT_CODE_VERIFIED : stubs.CONTRACT_CODE_UNVERIFIED, placeholderData: addressData?.is_verified ? stubs.CONTRACT_CODE_VERIFIED : stubs.CONTRACT_CODE_UNVERIFIED,
}, },
}); });
const { data, isPlaceholderData, isError } = contractQuery; const { data, isPlaceholderData, isError } = selectedItem.address_hash !== addressData.hash ? contractQuery : mainContractQuery;
const tabs = useContractDetailsTabs({ data, isLoading: isPlaceholderData, addressData, sourceAddress: selectedItem.address_hash }); const tabs = useContractDetailsTabs({ data, isLoading: isPlaceholderData, addressData, sourceAddress: selectedItem.address_hash });
const handleContractWasVerifiedMessage: SocketMessage.SmartContractWasVerified['handler'] = React.useCallback(() => {
queryClient.refetchQueries({
queryKey: getResourceKey('general:address', { pathParams: { hash: addressData.hash } }),
});
queryClient.refetchQueries({
queryKey: getResourceKey('general:contract', { pathParams: { hash: addressData.hash } }),
});
}, [ addressData.hash, queryClient ]);
useSocketMessage({
channel,
event: 'smart_contract_was_verified',
handler: handleContractWasVerifiedMessage,
});
if (isError) { if (isError) {
return <DataFetchAlert/>; return <DataFetchAlert/>;
} }
......
...@@ -2,6 +2,7 @@ import { useRouter } from 'next/router'; ...@@ -2,6 +2,7 @@ import { useRouter } from 'next/router';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useContractTabs from '../useContractTabs'; import useContractTabs from '../useContractTabs';
...@@ -9,7 +10,12 @@ const ContractDetails = () => { ...@@ -9,7 +10,12 @@ const ContractDetails = () => {
const router = useRouter(); const router = useRouter();
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const addressQuery = useApiQuery('general:address', { pathParams: { hash } }); const addressQuery = useApiQuery('general:address', { pathParams: { hash } });
const { tabs } = useContractTabs(addressQuery.data, false); const channel = useSocketChannel({
topic: `addresses:${ hash?.toLowerCase() }`,
isDisabled: !addressQuery.data,
});
const { tabs } = useContractTabs({ addressData: addressQuery.data, isEnabled: true, channel });
const content = tabs.find(({ id }) => id === 'contract_code')?.component; const content = tabs.find(({ id }) => id === 'contract_code')?.component;
return content ?? null; return content ?? null;
}; };
......
import { useRouter } from 'next/router'; import type { Channel } from 'phoenix';
import React from 'react'; import React from 'react';
import type { Address } from 'types/api/address'; import type { Address } from 'types/api/address';
import config from 'configs/app'; import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel';
import * as stubs from 'stubs/contract'; import * as stubs from 'stubs/contract';
import ContractDetails from 'ui/address/contract/ContractDetails'; import ContractDetails from 'ui/address/contract/ContractDetails';
import ContractMethodsCustom from 'ui/address/contract/methods/ContractMethodsCustom'; import ContractMethodsCustom from 'ui/address/contract/methods/ContractMethodsCustom';
...@@ -16,7 +14,7 @@ import ContractMethodsRegular from 'ui/address/contract/methods/ContractMethodsR ...@@ -16,7 +14,7 @@ import ContractMethodsRegular from 'ui/address/contract/methods/ContractMethodsR
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import type { CONTRACT_MAIN_TAB_IDS } from './utils'; import type { CONTRACT_MAIN_TAB_IDS } from './utils';
import { CONTRACT_DETAILS_TAB_IDS, CONTRACT_TAB_IDS } from './utils'; import { CONTRACT_DETAILS_TAB_IDS } from './utils';
interface ContractTab { interface ContractTab {
id: typeof CONTRACT_MAIN_TAB_IDS[number] | Array<typeof CONTRACT_MAIN_TAB_IDS[number]>; id: typeof CONTRACT_MAIN_TAB_IDS[number] | Array<typeof CONTRACT_MAIN_TAB_IDS[number]>;
...@@ -30,54 +28,43 @@ interface ReturnType { ...@@ -30,54 +28,43 @@ interface ReturnType {
isLoading: boolean; isLoading: boolean;
} }
export default function useContractTabs(data: Address | undefined, isPlaceholderData: boolean, hasMudTab: boolean = false): ReturnType { interface Props {
const [ isQueryEnabled, setIsQueryEnabled ] = React.useState(false); addressData: Address | undefined;
isEnabled: boolean;
const router = useRouter(); hasMudTab?: boolean;
const tab = getQueryParamString(router.query.tab); channel: Channel | undefined;
}
const isEnabled = Boolean(data?.hash) && data?.is_contract && !isPlaceholderData && CONTRACT_TAB_IDS.concat('contract' as never).includes(tab);
const enableQuery = React.useCallback(() => {
setIsQueryEnabled(true);
}, []);
export default function useContractTabs({ addressData, isEnabled, hasMudTab, channel }: Props): ReturnType {
const contractQuery = useApiQuery('general:contract', { const contractQuery = useApiQuery('general:contract', {
pathParams: { hash: data?.hash }, pathParams: { hash: addressData?.hash },
queryOptions: { queryOptions: {
enabled: isEnabled && isQueryEnabled, enabled: isEnabled,
refetchOnMount: false, refetchOnMount: false,
placeholderData: data?.is_verified ? stubs.CONTRACT_CODE_VERIFIED : stubs.CONTRACT_CODE_UNVERIFIED, placeholderData: addressData?.is_verified ? stubs.CONTRACT_CODE_VERIFIED : stubs.CONTRACT_CODE_UNVERIFIED,
}, },
}); });
const mudSystemsQuery = useApiQuery('general:mud_systems', { const mudSystemsQuery = useApiQuery('general:mud_systems', {
pathParams: { hash: data?.hash }, pathParams: { hash: addressData?.hash },
queryOptions: { queryOptions: {
enabled: isEnabled && isQueryEnabled && hasMudTab, enabled: isEnabled && hasMudTab,
refetchOnMount: false, refetchOnMount: false,
placeholderData: stubs.MUD_SYSTEMS, placeholderData: stubs.MUD_SYSTEMS,
}, },
}); });
const channel = useSocketChannel({
topic: `addresses:${ data?.hash?.toLowerCase() }`,
isDisabled: !isEnabled,
onJoin: enableQuery,
onSocketError: enableQuery,
});
const verifiedImplementations = React.useMemo(() => { const verifiedImplementations = React.useMemo(() => {
return data?.implementations?.filter(({ name, address_hash: addressHash }) => name && addressHash && addressHash !== data?.hash) || []; return addressData?.implementations?.filter(({ name, address_hash: addressHash }) => name && addressHash && addressHash !== addressData?.hash) || [];
}, [ data?.hash, data?.implementations ]); }, [ addressData?.hash, addressData?.implementations ]);
return React.useMemo(() => { return React.useMemo(() => {
return { return {
tabs: [ tabs: [
data && { addressData && {
id: 'contract_code' as const, id: 'contract_code' as const,
title: 'Code', title: 'Code',
component: <ContractDetails mainContractQuery={ contractQuery } channel={ channel } addressData={ data }/>, component: <ContractDetails mainContractQuery={ contractQuery } channel={ channel } addressData={ addressData }/>,
subTabs: CONTRACT_DETAILS_TAB_IDS as unknown as Array<string>, subTabs: CONTRACT_DETAILS_TAB_IDS as unknown as Array<string>,
}, },
contractQuery.data?.abi && { contractQuery.data?.abi && {
...@@ -92,7 +79,7 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder ...@@ -92,7 +79,7 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder
<ContractMethodsProxy <ContractMethodsProxy
implementations={ verifiedImplementations } implementations={ verifiedImplementations }
isLoading={ contractQuery.isPlaceholderData } isLoading={ contractQuery.isPlaceholderData }
proxyType={ data?.proxy_type } proxyType={ addressData?.proxy_type }
/> />
), ),
}, },
...@@ -112,7 +99,7 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder ...@@ -112,7 +99,7 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder
isLoading: contractQuery.isPlaceholderData, isLoading: contractQuery.isPlaceholderData,
}; };
}, [ }, [
data, addressData,
contractQuery, contractQuery,
channel, channel,
verifiedImplementations, verifiedImplementations,
......
...@@ -39,7 +39,6 @@ import AddressTokenTransfers from 'ui/address/AddressTokenTransfers'; ...@@ -39,7 +39,6 @@ import AddressTokenTransfers from 'ui/address/AddressTokenTransfers';
import AddressTxs from 'ui/address/AddressTxs'; import AddressTxs from 'ui/address/AddressTxs';
import AddressUserOps from 'ui/address/AddressUserOps'; import AddressUserOps from 'ui/address/AddressUserOps';
import AddressWithdrawals from 'ui/address/AddressWithdrawals'; import AddressWithdrawals from 'ui/address/AddressWithdrawals';
import useContractTabs from 'ui/address/contract/useContractTabs';
import { CONTRACT_TAB_IDS } from 'ui/address/contract/utils'; import { CONTRACT_TAB_IDS } from 'ui/address/contract/utils';
import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton'; import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton';
import AddressMetadataAlert from 'ui/address/details/AddressMetadataAlert'; import AddressMetadataAlert from 'ui/address/details/AddressMetadataAlert';
...@@ -172,12 +171,6 @@ const AddressPageContent = () => { ...@@ -172,12 +171,6 @@ const AddressPageContent = () => {
const xStarQuery = useFetchXStarScore({ hash }); const xStarQuery = useFetchXStarScore({ hash });
const contractTabs = useContractTabs(
addressQuery.data,
config.features.mudFramework.isEnabled ? (mudTablesCountQuery.isPlaceholderData || addressQuery.isPlaceholderData) : addressQuery.isPlaceholderData,
Boolean(config.features.mudFramework.isEnabled && mudTablesCountQuery.data && mudTablesCountQuery.data > 0),
);
const tabs: Array<TabItemRegular> = React.useMemo(() => { const tabs: Array<TabItemRegular> = React.useMemo(() => {
return [ return [
{ {
...@@ -203,9 +196,9 @@ const AddressPageContent = () => { ...@@ -203,9 +196,9 @@ const AddressPageContent = () => {
}, },
component: ( component: (
<AddressContract <AddressContract
tabs={ contractTabs.tabs } addressData={ addressQuery.data }
shouldRender={ !isTabsLoading } isLoading={ isTabsLoading }
isLoading={ contractTabs.isLoading } hasMudTab={ Boolean(config.features.mudFramework.isEnabled && mudTablesCountQuery.data && mudTablesCountQuery.data > 0) }
/> />
), ),
subTabs: CONTRACT_TAB_IDS, subTabs: CONTRACT_TAB_IDS,
...@@ -309,7 +302,6 @@ const AddressPageContent = () => { ...@@ -309,7 +302,6 @@ const AddressPageContent = () => {
}, [ }, [
addressQuery, addressQuery,
countersQuery, countersQuery,
contractTabs,
addressTabsCountersQuery.data, addressTabsCountersQuery.data,
userOpsAccountQuery.data, userOpsAccountQuery.data,
isTabsLoading, isTabsLoading,
......
...@@ -26,7 +26,6 @@ import Address3rdPartyWidgets from 'ui/address/Address3rdPartyWidgets'; ...@@ -26,7 +26,6 @@ import Address3rdPartyWidgets from 'ui/address/Address3rdPartyWidgets';
import useAddress3rdPartyWidgets from 'ui/address/address3rdPartyWidgets/useAddress3rdPartyWidgets'; import useAddress3rdPartyWidgets from 'ui/address/address3rdPartyWidgets/useAddress3rdPartyWidgets';
import AddressContract from 'ui/address/AddressContract'; import AddressContract from 'ui/address/AddressContract';
import AddressCsvExportLink from 'ui/address/AddressCsvExportLink'; import AddressCsvExportLink from 'ui/address/AddressCsvExportLink';
import useContractTabs from 'ui/address/contract/useContractTabs';
import { CONTRACT_TAB_IDS } from 'ui/address/contract/utils'; import { CONTRACT_TAB_IDS } from 'ui/address/contract/utils';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -171,8 +170,6 @@ const TokenPageContent = () => { ...@@ -171,8 +170,6 @@ const TokenPageContent = () => {
addressQuery.isPlaceholderData || addressQuery.isPlaceholderData ||
(address3rdPartyWidgets.isEnabled && address3rdPartyWidgets.configQuery.isPlaceholderData); (address3rdPartyWidgets.isEnabled && address3rdPartyWidgets.configQuery.isPlaceholderData);
const contractTabs = useContractTabs(addressQuery.data, addressQuery.isPlaceholderData);
const tabs: Array<TabItemRegular> = [ const tabs: Array<TabItemRegular> = [
hasInventoryTab ? { hasInventoryTab ? {
id: 'inventory', id: 'inventory',
...@@ -203,7 +200,7 @@ const TokenPageContent = () => { ...@@ -203,7 +200,7 @@ const TokenPageContent = () => {
return 'Contract'; return 'Contract';
}, },
component: <AddressContract tabs={ contractTabs.tabs } isLoading={ contractTabs.isLoading } shouldRender={ !isLoading }/>, component: <AddressContract addressData={ addressQuery.data } isLoading={ isLoading }/>,
subTabs: CONTRACT_TAB_IDS, subTabs: CONTRACT_TAB_IDS,
} : undefined, } : undefined,
(address3rdPartyWidgets.isEnabled && address3rdPartyWidgets.items.length > 0) ? { (address3rdPartyWidgets.isEnabled && address3rdPartyWidgets.items.length > 0) ? {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment