From 4ad3dd41b2a05313a9f03a0189bccf2822af71de Mon Sep 17 00:00:00 2001 From: dennisloh95 Date: Wed, 14 Jun 2023 18:28:20 +0800 Subject: [PATCH 01/73] feat: add l1_sent two direction sync --- .../src/schedule/tasks.service.ts | 205 ++++++++++++++---- 1 file changed, 166 insertions(+), 39 deletions(-) diff --git a/data-sync-service/src/schedule/tasks.service.ts b/data-sync-service/src/schedule/tasks.service.ts index 504ae4b0ff1b..f3fce42e6dae 100644 --- a/data-sync-service/src/schedule/tasks.service.ts +++ b/data-sync-service/src/schedule/tasks.service.ts @@ -6,6 +6,8 @@ import { L2IngestionService } from '../l2Ingestion/l2Ingestion.service'; import { ConfigService } from '@nestjs/config'; const L1_SENT = 'l1_sent_block_number'; +const L1_SENT_BACKTRACK = 'l1_sent_backtrack_block_number'; +const L1_SENT_BACKTRACK_START = 'l1_sent_backtrack_start_block_number'; const L1_RELAYED = 'l1_relayed_block_number'; const L2_SENT = 'l2_sent_block_number'; const L2_RELAYED = 'l2_relayed_block_number'; @@ -22,25 +24,37 @@ export class TasksService { private readonly l1IngestionService: L1IngestionService, private readonly l2IngestionService: L2IngestionService, @Inject(CACHE_MANAGER) private cacheManager: Cache, - private schedulerRegistry: SchedulerRegistry + private schedulerRegistry: SchedulerRegistry, ) { this.initCache(); const fixedL2ToL1TokenAddress0x000Bug = setInterval(async () => { try { - const result = await this.l2IngestionService.fixedL2ToL1TokenAddress0x000Bug(); + const result = + await this.l2IngestionService.fixedL2ToL1TokenAddress0x000Bug(); if (result.length <= 0) { console.log('deleteInterval fixedL2ToL1TokenAddress0x000Bug'); - this.schedulerRegistry.deleteInterval('fixedL2ToL1TokenAddress0x000Bug'); + this.schedulerRegistry.deleteInterval( + 'fixedL2ToL1TokenAddress0x000Bug', + ); } } catch (error) { this.logger.error(`error fixedL2ToL1TokenAddress0x000Bug: ${error}`); } }, 1000); - this.schedulerRegistry.addInterval('fixedL2ToL1TokenAddress0x000Bug', fixedL2ToL1TokenAddress0x000Bug); + this.schedulerRegistry.addInterval( + 'fixedL2ToL1TokenAddress0x000Bug', + fixedL2ToL1TokenAddress0x000Bug, + ); } private readonly logger = new Logger(TasksService.name); async initCache() { let l1_sent_block_number = await this.cacheManager.get(L1_SENT); + let l1_sent_backtrack_block_number = await this.cacheManager.get( + L1_SENT_BACKTRACK, + ); + let l1_sent_backtrack_start_block_number = await this.cacheManager.get( + L1_SENT_BACKTRACK_START, + ); let l1_relayed_block_number = await this.cacheManager.get(L1_RELAYED); let l2_sent_block_number = await this.cacheManager.get(L2_SENT); let l2_relayed_block_number = await this.cacheManager.get(L2_RELAYED); @@ -52,11 +66,18 @@ export class TasksService { (await this.l1IngestionService.getSentEventsBlockNumber()) || this.configService.get('L1_START_BLOCK_NUMBER'); } + if (!l1_sent_backtrack_block_number) { + l1_sent_backtrack_block_number = + (await this.l1IngestionService.getCurrentBlockNumber()) || + this.configService.get('L1_START_BACKTRACK_BLOCK_NUMBER'); + l1_sent_backtrack_start_block_number = l1_sent_backtrack_block_number; + } if (!l1_relayed_block_number) { l1_relayed_block_number = (await this.l1IngestionService.getRelayedEventsBlockNumber()) || this.configService.get('L1_START_BLOCK_NUMBER'); } + if (!l2_sent_block_number) { l2_sent_block_number = (await this.l2IngestionService.getSentEventsBlockNumber()) || @@ -79,12 +100,25 @@ export class TasksService { } if (!da_batch_index) { da_batch_index = - (await this.l1IngestionService.getLastBatchIndex() + 1) || - 1; + (await this.l1IngestionService.getLastBatchIndex()) + 1 || 1; } await this.cacheManager.set(L1_SENT, Number(l1_sent_block_number), { ttl: 0, }); + await this.cacheManager.set( + L1_SENT_BACKTRACK, + Number(l1_sent_backtrack_block_number), + { + ttl: 0, + }, + ); + await this.cacheManager.set( + L1_SENT_BACKTRACK_START, + Number(l1_sent_backtrack_start_block_number), + { + ttl: 0, + }, + ); await this.cacheManager.set(L1_RELAYED, Number(l1_relayed_block_number), { ttl: 0, }); @@ -105,13 +139,20 @@ export class TasksService { }); console.log('================end init cache================'); // TODO (Jayce) state batch missed data sync script - this.miss_data_script_start(9006135) + this.miss_data_script_start(9006135); } @Interval(2000) async l1_sent() { let end = 0; + let reach_backtrack = false; const currentBlockNumber = await this.l1IngestionService.getCurrentBlockNumber(); + const backtrackBlockNumber = Number( + await this.cacheManager.get(L1_SENT_BACKTRACK), + ); + const backtractStartBlock = Number( + await this.cacheManager.get(L1_SENT_BACKTRACK_START), + ); console.log('l1 sent currentBlockNumber: ', currentBlockNumber); const start = Number(await this.cacheManager.get(L1_SENT)); if (currentBlockNumber - start > SYNC_STEP) { @@ -122,22 +163,92 @@ export class TasksService { ? start - SYNC_STEP : currentBlockNumber; } + + if ( + backtrackBlockNumber > start && + backtrackBlockNumber - start < SYNC_STEP + ) { + end = backtrackBlockNumber - 1; + reach_backtrack = true; + } + if (currentBlockNumber > start + 1) { const result = await this.l1IngestionService.createSentEvents( start + 1, end, ); - const insertData = !result || result.length <= 0 ? [] : result[0].identifiers || [] + const insertData = + !result || result.length <= 0 ? [] : result[0].identifiers || []; this.logger.log( `sync [${insertData.length}] l1_sent_message_events from block [${start}] to [${end}]`, ); - await this.cacheManager.set(L1_SENT, end, { ttl: 0 }); + + if (reach_backtrack) { + await this.cacheManager.set(L1_SENT, backtractStartBlock, { ttl: 0 }); + } else { + await this.cacheManager.set(L1_SENT, end, { ttl: 0 }); + } } else { this.logger.log( `sync l1_sent finished and latest block number is: ${currentBlockNumber}`, ); } } + @Interval(3000) + async l1_sent_backtrack() { + let end = 0; + let start = 0; + const backtrackBlockNumber = Number( + await this.cacheManager.get(L1_SENT_BACKTRACK), + ); + if (!backtrackBlockNumber) { + this.logger.log(`backtrackBlockNumber not found`); + return; + } + const currentL1SentBlockNumber = Number( + await this.cacheManager.get(L1_SENT), + ); + console.log( + 'l1 backtrackBlockNumber: ', + backtrackBlockNumber, + 'l1 current sent block: ', + currentL1SentBlockNumber, + ); + + if ( + backtrackBlockNumber - currentL1SentBlockNumber < SYNC_STEP * 3 || + currentL1SentBlockNumber > backtrackBlockNumber + ) { + // triple SYNC_STEP to prevent execute during transaction in progress + this.logger.log(`sync l1_sent_backtrack finished`); + return; + } + end = backtrackBlockNumber; + + // above have anotther safe guard + if (end - currentL1SentBlockNumber > SYNC_STEP) { + start = end - SYNC_STEP; + } else { + start = + end - SYNC_STEP > currentL1SentBlockNumber + ? end - SYNC_STEP + : currentL1SentBlockNumber; + } + if (end > start + 1) { + const result = await this.l1IngestionService.createSentEvents( + start + 1, + end, + ); + const insertData = + !result || result.length <= 0 ? [] : result[0].identifiers || []; + this.logger.log( + `sync [${insertData.length}] l1_sent_message_events_backtrack from block [${start}] to [${end}]`, + ); + await this.cacheManager.set(L1_SENT_BACKTRACK, start, { ttl: 0 }); + } else { + this.logger.log(`sync l1_sent_backtrack finished`); + } + } @Interval(2000) async l1_relayed() { let end = 0; @@ -158,7 +269,8 @@ export class TasksService { start + 1, end, ); - const insertData = !result || result.length <= 0 ? [] : result[0].identifiers || [] + const insertData = + !result || result.length <= 0 ? [] : result[0].identifiers || []; this.logger.log( `sync [${insertData.length}] l1_relayed_message_events from block [${start}] to [${end}]`, ); @@ -189,7 +301,8 @@ export class TasksService { start + 1, end, ); - const insertData = !result || result.length <= 0 ? [] : result[0].identifiers || [] + const insertData = + !result || result.length <= 0 ? [] : result[0].identifiers || []; this.logger.log( `sync [${insertData.length}] l2_sent_message_events from block [${start}] to [${end}]`, ); @@ -220,7 +333,8 @@ export class TasksService { start + 1, end, ); - const insertData = !result || result.length <= 0 ? [] : result[0].identifiers || [] + const insertData = + !result || result.length <= 0 ? [] : result[0].identifiers || []; this.logger.log( `sync [${insertData.length}] l2_relayed_message_events from block [${start}] to [${end}]`, ); @@ -250,22 +364,21 @@ export class TasksService { : currentBlockNumber; } if (currentBlockNumber >= start + 1) { - const result = await this.l1IngestionService.createStateBatchesEvents( - start + 1, - end, - ).catch(e=> { - console.error(`insert state batch failed, number: ${start} ${end}`) - }); - if(result){ - const insertData = !result || result.length <= 0 ? [] : result[0].identifiers || [] + const result = await this.l1IngestionService + .createStateBatchesEvents(start + 1, end) + .catch((e) => { + console.error(`insert state batch failed, number: ${start} ${end}`); + }); + if (result) { + const insertData = + !result || result.length <= 0 ? [] : result[0].identifiers || []; this.logger.log( `sync [${insertData.length}] state_batch from block [${start}] to [${end}]`, ); await this.cacheManager.set(STATE_BATCH, end, { ttl: 0 }); - }else { - console.error('result insert state batch data failed') + } else { + console.error('result insert state batch data failed'); } - } else { this.logger.log( `sync state_batch finished and latest block number is: ${currentBlockNumber}`, @@ -273,21 +386,24 @@ export class TasksService { } } - async miss_data_script_start(block) { - console.log('-------------- start script , start block', block) - const result = await this.l1IngestionService.saveStateBatchMissedScript(block).catch(e=> { - console.error(`insert state batch failed,`) - }); - console.log('list sync completed, the next block:', result) - if(result && result < 9146135){ - this.miss_data_script_start(result) - }else { - console.error('result insert state batch data failed, or sync completed!', result) + console.log('-------------- start script , start block', block); + const result = await this.l1IngestionService + .saveStateBatchMissedScript(block) + .catch((e) => { + console.error(`insert state batch failed,`); + }); + console.log('list sync completed, the next block:', result); + if (result && result < 9146135) { + this.miss_data_script_start(result); + } else { + console.error( + 'result insert state batch data failed, or sync completed!', + result, + ); } } - /* @Interval(2000) async txn_batch() { let end = 0; @@ -346,15 +462,26 @@ export class TasksService { @Interval(5000) async eigen_da_batch_txns() { try { - const fromStoreNumber = Number(await this.cacheManager.get(DA_BATCH_INDEX)); - console.log('[syncEigenDaBatch] start eigenda service fromStoreNumber: ', fromStoreNumber); - const result = await this.l1IngestionService.syncEigenDaBatch(fromStoreNumber); + const fromStoreNumber = Number( + await this.cacheManager.get(DA_BATCH_INDEX), + ); + console.log( + '[syncEigenDaBatch] start eigenda service fromStoreNumber: ', + fromStoreNumber, + ); + const result = await this.l1IngestionService.syncEigenDaBatch( + fromStoreNumber, + ); if (result) { console.log('[syncEigenDaBatch] add DA_BATCH_INDEX'); - await this.cacheManager.set(DA_BATCH_INDEX, fromStoreNumber + 1, { ttl: 0 }); + await this.cacheManager.set(DA_BATCH_INDEX, fromStoreNumber + 1, { + ttl: 0, + }); } } catch (error) { - this.logger.error(`[syncEigenDaBatch] error eigen da batches err: ${error}`); + this.logger.error( + `[syncEigenDaBatch] error eigen da batches err: ${error}`, + ); } } } From 3659b86a5061608ac01e8807b09f45b6c201f2b4 Mon Sep 17 00:00:00 2001 From: dennisloh95 Date: Thu, 15 Jun 2023 14:52:46 +0800 Subject: [PATCH 02/73] feat: revamp sync logic, sync 1 from block x to y and stop, sync 2 from block y to latest --- .../src/schedule/tasks.service.ts | 117 ++++++++---------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/data-sync-service/src/schedule/tasks.service.ts b/data-sync-service/src/schedule/tasks.service.ts index f3fce42e6dae..5083d45d8be8 100644 --- a/data-sync-service/src/schedule/tasks.service.ts +++ b/data-sync-service/src/schedule/tasks.service.ts @@ -6,8 +6,8 @@ import { L2IngestionService } from '../l2Ingestion/l2Ingestion.service'; import { ConfigService } from '@nestjs/config'; const L1_SENT = 'l1_sent_block_number'; -const L1_SENT_BACKTRACK = 'l1_sent_backtrack_block_number'; -const L1_SENT_BACKTRACK_START = 'l1_sent_backtrack_start_block_number'; +const L1_SENT_CURRENT_START = 'l1_sent_current_start_block_number'; +const L1_SENT_CURRENT = 'l1_sent_current_block_number'; const L1_RELAYED = 'l1_relayed_block_number'; const L2_SENT = 'l2_sent_block_number'; const L2_RELAYED = 'l2_relayed_block_number'; @@ -27,6 +27,7 @@ export class TasksService { private schedulerRegistry: SchedulerRegistry, ) { this.initCache(); + const fixedL2ToL1TokenAddress0x000Bug = setInterval(async () => { try { const result = @@ -46,14 +47,15 @@ export class TasksService { fixedL2ToL1TokenAddress0x000Bug, ); } + private readonly logger = new Logger(TasksService.name); async initCache() { let l1_sent_block_number = await this.cacheManager.get(L1_SENT); - let l1_sent_backtrack_block_number = await this.cacheManager.get( - L1_SENT_BACKTRACK, + let l1_sent_current_start_block_number = await this.cacheManager.get( + L1_SENT_CURRENT_START, ); - let l1_sent_backtrack_start_block_number = await this.cacheManager.get( - L1_SENT_BACKTRACK_START, + let l1_sent_current_block_number = await this.cacheManager.get( + L1_SENT_CURRENT, ); let l1_relayed_block_number = await this.cacheManager.get(L1_RELAYED); let l2_sent_block_number = await this.cacheManager.get(L2_SENT); @@ -66,11 +68,11 @@ export class TasksService { (await this.l1IngestionService.getSentEventsBlockNumber()) || this.configService.get('L1_START_BLOCK_NUMBER'); } - if (!l1_sent_backtrack_block_number) { - l1_sent_backtrack_block_number = + if (!l1_sent_current_block_number) { + l1_sent_current_block_number = (await this.l1IngestionService.getCurrentBlockNumber()) || this.configService.get('L1_START_BACKTRACK_BLOCK_NUMBER'); - l1_sent_backtrack_start_block_number = l1_sent_backtrack_block_number; + l1_sent_current_start_block_number = l1_sent_current_block_number; } if (!l1_relayed_block_number) { l1_relayed_block_number = @@ -106,15 +108,15 @@ export class TasksService { ttl: 0, }); await this.cacheManager.set( - L1_SENT_BACKTRACK, - Number(l1_sent_backtrack_block_number), + L1_SENT_CURRENT, + Number(l1_sent_current_block_number), { ttl: 0, }, ); await this.cacheManager.set( - L1_SENT_BACKTRACK_START, - Number(l1_sent_backtrack_start_block_number), + L1_SENT_CURRENT_START, + Number(l1_sent_current_start_block_number), { ttl: 0, }, @@ -141,20 +143,22 @@ export class TasksService { // TODO (Jayce) state batch missed data sync script this.miss_data_script_start(9006135); } - @Interval(2000) + + @Interval('l1_sent', 2000) async l1_sent() { let end = 0; - let reach_backtrack = false; + let reachSyncLatest = false; + const currentInterval = this.schedulerRegistry.getInterval('l1_sent'); const currentBlockNumber = await this.l1IngestionService.getCurrentBlockNumber(); - const backtrackBlockNumber = Number( - await this.cacheManager.get(L1_SENT_BACKTRACK), + const l1SyncLatestStartBlock = Number( + await this.cacheManager.get(L1_SENT_CURRENT_START), ); - const backtractStartBlock = Number( - await this.cacheManager.get(L1_SENT_BACKTRACK_START), + console.log( + `l1 sent currentBlockNumber: , ${currentBlockNumber}, latest start block is ${l1SyncLatestStartBlock}`, ); - console.log('l1 sent currentBlockNumber: ', currentBlockNumber); const start = Number(await this.cacheManager.get(L1_SENT)); + if (currentBlockNumber - start > SYNC_STEP) { end = start + SYNC_STEP; } else { @@ -165,11 +169,14 @@ export class TasksService { } if ( - backtrackBlockNumber > start && - backtrackBlockNumber - start < SYNC_STEP + l1SyncLatestStartBlock > start && + l1SyncLatestStartBlock - start < SYNC_STEP ) { - end = backtrackBlockNumber - 1; - reach_backtrack = true; + end = l1SyncLatestStartBlock - 1; + reachSyncLatest = true; + console.log( + `l1 sent reach: , ${start} to ${end}. latest start block is ${l1SyncLatestStartBlock}`, + ); } if (currentBlockNumber > start + 1) { @@ -183,8 +190,10 @@ export class TasksService { `sync [${insertData.length}] l1_sent_message_events from block [${start}] to [${end}]`, ); - if (reach_backtrack) { - await this.cacheManager.set(L1_SENT, backtractStartBlock, { ttl: 0 }); + if (reachSyncLatest) { + this.logger.log(`sync l1_sent done, end job.`); + clearInterval(currentInterval); + return; } else { await this.cacheManager.set(L1_SENT, end, { ttl: 0 }); } @@ -194,47 +203,28 @@ export class TasksService { ); } } - @Interval(3000) - async l1_sent_backtrack() { + @Interval(2000) + async l1_sent_from_latest() { let end = 0; let start = 0; - const backtrackBlockNumber = Number( - await this.cacheManager.get(L1_SENT_BACKTRACK), - ); - if (!backtrackBlockNumber) { - this.logger.log(`backtrackBlockNumber not found`); - return; - } - const currentL1SentBlockNumber = Number( - await this.cacheManager.get(L1_SENT), + const currentBlockNumber = Number( + await this.l1IngestionService.getCurrentBlockNumber(), ); + start = Number(await this.cacheManager.get(L1_SENT_CURRENT)); console.log( - 'l1 backtrackBlockNumber: ', - backtrackBlockNumber, - 'l1 current sent block: ', - currentL1SentBlockNumber, + `l1 sentFromLatest currentBlockNumber: , ${currentBlockNumber} `, ); - if ( - backtrackBlockNumber - currentL1SentBlockNumber < SYNC_STEP * 3 || - currentL1SentBlockNumber > backtrackBlockNumber - ) { - // triple SYNC_STEP to prevent execute during transaction in progress - this.logger.log(`sync l1_sent_backtrack finished`); - return; - } - end = backtrackBlockNumber; - - // above have anotther safe guard - if (end - currentL1SentBlockNumber > SYNC_STEP) { - start = end - SYNC_STEP; + if (currentBlockNumber - start > SYNC_STEP) { + end = start + SYNC_STEP; } else { - start = - end - SYNC_STEP > currentL1SentBlockNumber - ? end - SYNC_STEP - : currentL1SentBlockNumber; + end = + start - SYNC_STEP > currentBlockNumber + ? start - SYNC_STEP + : currentBlockNumber; } - if (end > start + 1) { + + if (currentBlockNumber > start + 1) { const result = await this.l1IngestionService.createSentEvents( start + 1, end, @@ -242,11 +232,14 @@ export class TasksService { const insertData = !result || result.length <= 0 ? [] : result[0].identifiers || []; this.logger.log( - `sync [${insertData.length}] l1_sent_message_events_backtrack from block [${start}] to [${end}]`, + `sync [${insertData.length}] l1_sent_from_latest_message_events from block [${start}] to [${end}]`, ); - await this.cacheManager.set(L1_SENT_BACKTRACK, start, { ttl: 0 }); + + await this.cacheManager.set(L1_SENT_CURRENT, end, { ttl: 0 }); } else { - this.logger.log(`sync l1_sent_backtrack finished`); + this.logger.log( + `sync l1_sent_from_latest finished and latest block number is: ${currentBlockNumber}`, + ); } } @Interval(2000) From 7bcccf897a7d23c107555102c1eece1b19917b05 Mon Sep 17 00:00:00 2001 From: dennisloh95 Date: Thu, 15 Jun 2023 16:41:16 +0800 Subject: [PATCH 03/73] feat: enhance l1_sent_history code --- .../src/schedule/tasks.service.ts | 45 +++++++------------ 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/data-sync-service/src/schedule/tasks.service.ts b/data-sync-service/src/schedule/tasks.service.ts index 5083d45d8be8..cc731a3b16a7 100644 --- a/data-sync-service/src/schedule/tasks.service.ts +++ b/data-sync-service/src/schedule/tasks.service.ts @@ -144,34 +144,22 @@ export class TasksService { this.miss_data_script_start(9006135); } - @Interval('l1_sent', 2000) - async l1_sent() { + @Interval('l1_sent_history', 2000) + async l1_sent_history() { + // sync history block, stop when sync reach latest starting block. let end = 0; let reachSyncLatest = false; - const currentInterval = this.schedulerRegistry.getInterval('l1_sent'); - const currentBlockNumber = - await this.l1IngestionService.getCurrentBlockNumber(); + const currentInterval = + this.schedulerRegistry.getInterval('l1_sent_history'); const l1SyncLatestStartBlock = Number( await this.cacheManager.get(L1_SENT_CURRENT_START), ); - console.log( - `l1 sent currentBlockNumber: , ${currentBlockNumber}, latest start block is ${l1SyncLatestStartBlock}`, - ); + console.log(`l1 sent to latest start block: , ${l1SyncLatestStartBlock}`); const start = Number(await this.cacheManager.get(L1_SENT)); - if (currentBlockNumber - start > SYNC_STEP) { + if (l1SyncLatestStartBlock - start > SYNC_STEP) { end = start + SYNC_STEP; } else { - end = - start - SYNC_STEP > currentBlockNumber - ? start - SYNC_STEP - : currentBlockNumber; - } - - if ( - l1SyncLatestStartBlock > start && - l1SyncLatestStartBlock - start < SYNC_STEP - ) { end = l1SyncLatestStartBlock - 1; reachSyncLatest = true; console.log( @@ -179,7 +167,7 @@ export class TasksService { ); } - if (currentBlockNumber > start + 1) { + if (l1SyncLatestStartBlock > start + 1) { const result = await this.l1IngestionService.createSentEvents( start + 1, end, @@ -187,11 +175,11 @@ export class TasksService { const insertData = !result || result.length <= 0 ? [] : result[0].identifiers || []; this.logger.log( - `sync [${insertData.length}] l1_sent_message_events from block [${start}] to [${end}]`, + `sync [${insertData.length}] l1_sent_history_message_events from block [${start}] to [${end}]`, ); if (reachSyncLatest) { - this.logger.log(`sync l1_sent done, end job.`); + this.logger.log(`sync l1_sent_history done, end job.`); clearInterval(currentInterval); return; } else { @@ -199,21 +187,20 @@ export class TasksService { } } else { this.logger.log( - `sync l1_sent finished and latest block number is: ${currentBlockNumber}`, + `sync l1_sent_history finished and latest block number is: ${l1SyncLatestStartBlock}`, ); } } @Interval(2000) - async l1_sent_from_latest() { + async l1_sent_latest() { + // sync from latest block let end = 0; let start = 0; const currentBlockNumber = Number( await this.l1IngestionService.getCurrentBlockNumber(), ); start = Number(await this.cacheManager.get(L1_SENT_CURRENT)); - console.log( - `l1 sentFromLatest currentBlockNumber: , ${currentBlockNumber} `, - ); + console.log(`l1 sentLatest currentBlockNumber: , ${currentBlockNumber} `); if (currentBlockNumber - start > SYNC_STEP) { end = start + SYNC_STEP; @@ -232,13 +219,13 @@ export class TasksService { const insertData = !result || result.length <= 0 ? [] : result[0].identifiers || []; this.logger.log( - `sync [${insertData.length}] l1_sent_from_latest_message_events from block [${start}] to [${end}]`, + `sync [${insertData.length}] l1_sent_latest_message_events from block [${start}] to [${end}]`, ); await this.cacheManager.set(L1_SENT_CURRENT, end, { ttl: 0 }); } else { this.logger.log( - `sync l1_sent_from_latest finished and latest block number is: ${currentBlockNumber}`, + `sync l1_sent_latest finished and latest block number is: ${currentBlockNumber}`, ); } } From 156568e34e0fa9e17e01a013f5b4ec5b65d7bc7f Mon Sep 17 00:00:00 2001 From: dennisloh95 Date: Mon, 10 Jul 2023 16:00:33 +0800 Subject: [PATCH 04/73] chore: update locale string --- .../assets/js/lib/async_listing_load.js | 5 +++-- apps/block_scout_web/assets/js/locale.js | 7 ++++--- .../templates/block/overview.html.eex | 2 +- .../_pagination_container.html.eex | 2 +- .../templates/transaction/overview.html.eex | 8 ++++---- .../views/da_batch_transaction_view.ex | 2 +- .../block_scout_web/views/eigenda_batch_view.ex | 2 +- .../block_scout_web/views/l1_to_l2_txn_view.ex | 2 +- .../block_scout_web/views/l2_to_l1_txn_view.ex | 2 +- .../views/state_batch_transaction_view.ex | 2 +- .../block_scout_web/views/state_batch_view.ex | 2 +- .../block_scout_web/views/transaction_view.ex | 2 +- .../lib/block_scout_web/views/txn_batch_view.ex | 2 +- apps/block_scout_web/priv/gettext/default.pot | 15 +++++++++++++++ .../priv/gettext/en/LC_MESSAGES/default.po | 15 +++++++++++++++ .../priv/gettext/ja/LC_MESSAGES/default.po | 17 ++++++++++++++++- .../priv/gettext/ko/LC_MESSAGES/default.po | 15 +++++++++++++++ .../priv/gettext/ru/LC_MESSAGES/default.po | 15 +++++++++++++++ .../priv/gettext/zh/LC_MESSAGES/default.po | 17 ++++++++++++++++- 19 files changed, 113 insertions(+), 21 deletions(-) diff --git a/apps/block_scout_web/assets/js/lib/async_listing_load.js b/apps/block_scout_web/assets/js/lib/async_listing_load.js index 61cb0282a3b5..b55778e8d108 100644 --- a/apps/block_scout_web/assets/js/lib/async_listing_load.js +++ b/apps/block_scout_web/assets/js/lib/async_listing_load.js @@ -252,16 +252,17 @@ export const elements = { }, '[data-async-listing] [data-page-number]': { render ($el, state) { + const pageLang = $el.data('page-lang') if (state.emptyResponse) { return $el.hide() } $el.show() if (state.pagesStack.length === 0) { - return $el.text('Page 1') + return $el.text(`${pageLang} 1`) } - $el.text('Page ' + state.pagesStack.length) + $el.text(`${pageLang} ` + state.pagesStack.length) } }, '[data-async-listing] [data-loading-button]': { diff --git a/apps/block_scout_web/assets/js/locale.js b/apps/block_scout_web/assets/js/locale.js index 94f198c6dd00..adcb38fd5cb2 100644 --- a/apps/block_scout_web/assets/js/locale.js +++ b/apps/block_scout_web/assets/js/locale.js @@ -1,8 +1,9 @@ import moment from 'moment' import numeral from 'numeral' import 'numeral/locales' +import Cookies from 'js-cookie' -export const locale = 'en' +export const locale = Cookies.get('locale') || 'en' -moment.locale(locale) -numeral.locale(locale) +moment.locale('en') +numeral.locale('en') diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex index 9dfc20943e45..c8fc8dec6170 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex @@ -26,7 +26,7 @@ -
  • <%= gettext "Block" %> <%= @block.number %> +
  • <%= gettext "Block" %> <%= @block.number %>
  • -
  • <%= gettext "Page" %> <%= assigns[:cur_page_number] || "" %> <%= if assigns[:total_pages_number] do %> <%= gettext "of" %> <%= assigns[:total_pages_number] || "undefined" %><% end %>
  • +
  • <%= gettext "Page" %> <%= assigns[:cur_page_number] || "" %> <%= if assigns[:total_pages_number] do %> <%= gettext "of" %> <%= assigns[:total_pages_number] || "undefined" %><% end %>
  • - Accepted on L2 + <%= gettext "Accepted on L2" %> - Rollup to L1 + <%= gettext "Rollup to L1" %> - Finalized on L1 + <%= gettext "Finalized on L1" %> - Challenge Period Passed + <%= gettext "Challenge Period Passed" %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/da_batch_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/da_batch_transaction_view.ex index df68886d3fb7..b362d29eae76 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/da_batch_transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/da_batch_transaction_view.ex @@ -243,7 +243,7 @@ defmodule BlockScoutWeb.DaBatchTransactionView do avg_time |> Duration.to_seconds() - {:ok, "<= #{avg_time_in_secs} seconds"} + {:ok, "<= #{avg_time_in_secs} #{gettext("seconds")}"} end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/eigenda_batch_view.ex b/apps/block_scout_web/lib/block_scout_web/views/eigenda_batch_view.ex index c93a197f9fd3..55749a352263 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/eigenda_batch_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/eigenda_batch_view.ex @@ -243,7 +243,7 @@ defmodule BlockScoutWeb.EigendaBatchView do avg_time |> Duration.to_seconds() - {:ok, "<= #{avg_time_in_secs} seconds"} + {:ok, "<= #{avg_time_in_secs} #{gettext("seconds")}"} end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/l1_to_l2_txn_view.ex b/apps/block_scout_web/lib/block_scout_web/views/l1_to_l2_txn_view.ex index 3605f455737e..6bdb9c1a647a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/l1_to_l2_txn_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/l1_to_l2_txn_view.ex @@ -247,7 +247,7 @@ defmodule BlockScoutWeb.L1ToL2TxnView do avg_time |> Duration.to_seconds() - {:ok, "<= #{avg_time_in_secs} seconds"} + {:ok, "<= #{avg_time_in_secs} #{gettext("seconds")}"} end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/l2_to_l1_txn_view.ex b/apps/block_scout_web/lib/block_scout_web/views/l2_to_l1_txn_view.ex index 616e55754b3e..30cfbe6eec43 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/l2_to_l1_txn_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/l2_to_l1_txn_view.ex @@ -230,7 +230,7 @@ defmodule BlockScoutWeb.L2ToL1TxnView do avg_time |> Duration.to_seconds() - {:ok, "<= #{avg_time_in_secs} seconds"} + {:ok, "<= #{avg_time_in_secs} #{gettext("seconds")}"} end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/state_batch_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/state_batch_transaction_view.ex index 30a22bfd9232..16a4bf73aac5 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/state_batch_transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/state_batch_transaction_view.ex @@ -228,7 +228,7 @@ require Logger avg_time |> Duration.to_seconds() - {:ok, "<= #{avg_time_in_secs} seconds"} + {:ok, "<= #{avg_time_in_secs} #{gettext("seconds")}"} end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/state_batch_view.ex b/apps/block_scout_web/lib/block_scout_web/views/state_batch_view.ex index ecbf126e55d2..7c24b92788da 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/state_batch_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/state_batch_view.ex @@ -238,7 +238,7 @@ defmodule BlockScoutWeb.StateBatchView do avg_time |> Duration.to_seconds() - {:ok, "<= #{avg_time_in_secs} seconds"} + {:ok, "<= #{avg_time_in_secs} #{gettext("seconds")}"} end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index 509cb1d69448..0bc89d9fea0d 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -228,7 +228,7 @@ require Logger avg_time |> Duration.to_seconds() - {:ok, "<= #{avg_time_in_secs} seconds"} + {:ok, "<= #{avg_time_in_secs} #{gettext("seconds")}"} end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/txn_batch_view.ex b/apps/block_scout_web/lib/block_scout_web/views/txn_batch_view.ex index fbd987a30edd..30683673b1c2 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/txn_batch_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/txn_batch_view.ex @@ -243,7 +243,7 @@ defmodule BlockScoutWeb.TxnBatchView do avg_time |> Duration.to_seconds() - {:ok, "<= #{avg_time_in_secs} seconds"} + {:ok, "<= #{avg_time_in_secs} #{gettext("seconds")}"} end end diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 0deb7fa05629..2f07e168f451 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -5224,4 +5224,19 @@ msgid "20 Bytes - address to check for balance" msgstr "" msgid "Integer block number, or the string \"latest\", \"earliest\" or \"pending\"" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "Accepted on L2" +msgstr "" + +msgid "Rollup to L1" +msgstr "" + +msgid "Finalized on L1" +msgstr "" + +msgid "Challenge Period Passed" msgstr "" \ No newline at end of file diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 3dbad9f3f985..bfd9070dd2ba 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -5230,4 +5230,19 @@ msgid "20 Bytes - address to check for balance" msgstr "" msgid "Integer block number, or the string \"latest\", \"earliest\" or \"pending\"" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "Accepted on L2" +msgstr "" + +msgid "Rollup to L1" +msgstr "" + +msgid "Finalized on L1" +msgstr "" + +msgid "Challenge Period Passed" msgstr "" \ No newline at end of file diff --git a/apps/block_scout_web/priv/gettext/ja/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/ja/LC_MESSAGES/default.po index 0305da85ec88..a7de11bb63cf 100644 --- a/apps/block_scout_web/priv/gettext/ja/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/ja/LC_MESSAGES/default.po @@ -5235,4 +5235,19 @@ msgid "20 Bytes - address to check for balance" msgstr "20バイト - 残高を確認するアドレス" msgid "Integer block number, or the string \"latest\", \"earliest\" or \"pending\"" -msgstr "整数ブロック番号、または文字列 \"最新\"、\"最古\"、または \"保留中\"" \ No newline at end of file +msgstr "整数ブロック番号、または文字列 \"最新\"、\"最古\"、または \"保留中\"" + +msgid "seconds" +msgstr "秒" + +msgid "Accepted on L2" +msgstr "L2で受け付けられました" + +msgid "Rollup to L1" +msgstr "L1まで巻き上げる" + +msgid "Finalized on L1" +msgstr "L1で最終決定" + +msgid "Challenge Period Passed" +msgstr "チャレンジ期間が終了しました" \ No newline at end of file diff --git a/apps/block_scout_web/priv/gettext/ko/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/ko/LC_MESSAGES/default.po index bf13eced1159..214f44a5cf34 100644 --- a/apps/block_scout_web/priv/gettext/ko/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/ko/LC_MESSAGES/default.po @@ -5236,3 +5236,18 @@ msgstr "20바이트 - 잔액을 확인할 주소" msgid "Integer block number, or the string \"latest\", \"earliest\" or \"pending\"" msgstr "정수 블록 번호 또는 \"latest\", \"earliest\" 또는 \"pending\" 이라는 문자열" + +msgid "seconds" +msgstr "초" + +msgid "Accepted on L2" +msgstr "L2에서 수락되었습니다" + +msgid "Rollup to L1" +msgstr "L1로 롤업" + +msgid "Finalized on L1" +msgstr "L1로 확정되었습니다" + +msgid "Challenge Period Passed" +msgstr "도전 기간이 지났습니다" \ No newline at end of file diff --git a/apps/block_scout_web/priv/gettext/ru/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/ru/LC_MESSAGES/default.po index 1f9a9760b9cf..a72a1ff9003f 100644 --- a/apps/block_scout_web/priv/gettext/ru/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/ru/LC_MESSAGES/default.po @@ -5246,3 +5246,18 @@ msgstr "20 байт - адрес для проверки баланса" msgid "Integer block number, or the string \"latest\", \"earliest\" or \"pending\"" msgstr "Блок номер целого числа или строка \"latest\", \"earliest\" или \"pending\"" + +msgid "seconds" +msgstr "секунды" + +msgid "Accepted on L2" +msgstr "Принято на уровне L2" + +msgid "Rollup to L1" +msgstr "Свернуть до L1" + +msgid "Finalized on L1" +msgstr "Окончательно согласовано на L1" + +msgid "Challenge Period Passed" +msgstr "Период испытаний прошел" \ No newline at end of file diff --git a/apps/block_scout_web/priv/gettext/zh/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/zh/LC_MESSAGES/default.po index afaff9027f90..21ba6e6e2926 100644 --- a/apps/block_scout_web/priv/gettext/zh/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/zh/LC_MESSAGES/default.po @@ -5237,4 +5237,19 @@ msgid "20 Bytes - address to check for balance" msgstr "20字节 - 地址用于检查余额" msgid "Integer block number, or the string \"latest\", \"earliest\" or \"pending\"" -msgstr "整数块数字,或字符串\"latest\",\"earliest\"或\"pending\"" \ No newline at end of file +msgstr "整数块数字,或字符串\"latest\",\"earliest\"或\"pending\"" + +msgid "seconds" +msgstr "秒" + +msgid "Accepted on L2" +msgstr "在L2上接受" + +msgid "Rollup to L1" +msgstr "Rollup 到 L1" + +msgid "Finalized on L1" +msgstr "最终确认在L1" + +msgid "Challenge Period Passed" +msgstr "挑战期已过" \ No newline at end of file From d5b67cbaeccb37a63c581d7c7edb30b40820dcdb Mon Sep 17 00:00:00 2001 From: Jaycelv Date: Mon, 3 Jul 2023 20:24:58 +0800 Subject: [PATCH 05/73] fix: average block time -> 2000ms --- apps/indexer/lib/indexer/block/realtime/fetcher.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index 6dce096e60e7..492e7099d4d2 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -177,7 +177,10 @@ defmodule Indexer.Block.Realtime.Fetcher do polling_period = case AverageBlockTime.average_block_time() do {:error, :disabled} -> 2_000 - block_time -> round(Duration.to_milliseconds(block_time) / 2) + block_time -> 1000 + #Logger.info("----------") + #Logger.info("#{inspect(round(Duration.to_milliseconds(block_time) / 2))}") + #round(Duration.to_milliseconds(block_time) / 2) end safe_polling_period = max(polling_period, @minimum_safe_polling_period) From 7c096b40baf60fcca514d5b5886d43e36a9f7325 Mon Sep 17 00:00:00 2001 From: dennisloh95 Date: Wed, 12 Jul 2023 23:49:49 +0800 Subject: [PATCH 06/73] feat: use tabs to access contract read write --- apps/block_scout_web/assets/.prettierrc | 6 + .../assets/css/theme/_mantle-theme.scss | 245 +++++++++----- .../assets/js/lib/custom_components.js | 3 + .../js/lib/smart_contract/common_helpers.js | 261 ++++++++++----- .../assets/js/lib/smart_contract/functions.js | 89 ++++-- .../templates/address/_tabs.html.eex | 33 +- .../templates/address_contract/_code.html.eex | 245 ++++++++++++++ .../templates/address_contract/index.html.eex | 299 ++++-------------- .../_read_contract.html.eex | 50 +++ .../_write_contract.html.eex | 47 +++ .../_connect_container.html.eex | 32 +- .../views/address_contract_view.ex | 55 +++- .../views/address_read_contract_view.ex | 73 +++++ .../views/address_write_contract_view.ex | 65 ++++ 14 files changed, 1039 insertions(+), 464 deletions(-) create mode 100644 apps/block_scout_web/assets/.prettierrc create mode 100644 apps/block_scout_web/assets/js/lib/custom_components.js create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract/_code.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_read_contract.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_write_contract.html.eex diff --git a/apps/block_scout_web/assets/.prettierrc b/apps/block_scout_web/assets/.prettierrc new file mode 100644 index 000000000000..27072444178a --- /dev/null +++ b/apps/block_scout_web/assets/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "semi": false, + "trailingComma": "none", + "insertSpaceBeforeFunctionParenthesis": "true" +} diff --git a/apps/block_scout_web/assets/css/theme/_mantle-theme.scss b/apps/block_scout_web/assets/css/theme/_mantle-theme.scss index e19711cb91be..caf11115979a 100644 --- a/apps/block_scout_web/assets/css/theme/_mantle-theme.scss +++ b/apps/block_scout_web/assets/css/theme/_mantle-theme.scss @@ -12,13 +12,86 @@ $dark-primary-alternate: #65b3ae !default; $dropdown-menu-item-hover-background-dark: #305a57; $dropdown-menu-background-dark: #3f7470; a { - text-decoration: none !important; + text-decoration: none !important; } -body{ +body { overscroll-behavior: none; } +.tab-content { + .card-misc-container { + padding: 0; + margin-bottom: 20px; + } + .connect-container { + .web3-btn { + margin-left: 0 !important; + background-color: #f8f8f9; + border: 1px solid #cfd2d4; + border-radius: 8px; + color: #2b3034; + gap: 10px; + display: flex; + align-items: center; + padding: 10px 16px; + line-height: 1; + height: 36px; + p[connected-to-address] { + margin: 0; + } + .status-dot { + width: 10px; + height: 10px; + background: #e74242; + border-radius: 100%; + display: block; + &.active { + background: #3eb66a; + } + } + } + } +} + +.card-tabs { + border-bottom: 0; + margin: 20px 20px 0; + padding: 12px; + border-radius: 12px; + background-color: #f8f8f9; + gap: 20px; + + .card-tab { + height: 40px; + border-radius: 8px; + background-color: transparent; + color: #15181c; + &.active { + background-color: #65b3ae; + font-weight: bold; + .fa-check-circle { + color: #15181c; + } + } + } +} + +.contract-tabs { + ul { + display: flex; + border-bottom: 0; + gap: 12px; + .nav-item { + .nav-link { + padding: 7px 17px; + border-radius: 8px; + color: #15181c; + } + } + } +} + .dashboard-banner-info-container { width: 100%; display: flex; @@ -36,8 +109,8 @@ body{ .navbar.navbar-primary .navbar-nav .nav-link { .nav-link-icon { svg { - &[data-icon="token"], - &[data-icon="user"] { + &[data-icon='token'], + &[data-icon='user'] { path { fill: transparent; stroke: #21413f; @@ -47,8 +120,8 @@ body{ } &:hover { svg { - &[data-icon="token"], - &[data-icon="user"] { + &[data-icon='token'], + &[data-icon='user'] { path { stroke: #fff; } @@ -57,7 +130,8 @@ body{ } } -.button-primary, .btn-full-primary { +.button-primary, +.btn-full-primary { border-radius: 12px; } @@ -81,19 +155,19 @@ body{ opacity: 0.8; } - [data-icon="moon"] { + [data-icon='moon'] { display: none; - path{ - stroke:#65B3AE; + path { + stroke: #65b3ae; } } - [data-icon="sun"] { + [data-icon='sun'] { display: inline-block; - path{ - stroke:#21413F; + path { + stroke: #21413f; } - circle{ - stroke:#21413F; + circle { + stroke: #21413f; } } } @@ -197,10 +271,10 @@ body{ color: #fff; .dark-mode-changer { - [data-icon="moon"] { + [data-icon='moon'] { display: inline-block; } - [data-icon="sun"] { + [data-icon='sun'] { display: none; } } @@ -225,30 +299,30 @@ body{ .navbar.navbar-primary .navbar-nav .nav-link { color: $dark-text-content-color; - .dropdown-toggle-icon{ - path{ - stroke:$dark-text-content-color; + .dropdown-toggle-icon { + path { + stroke: $dark-text-content-color; } } - &:hover{ - .dropdown-toggle-icon{ - path{ - stroke:white; + &:hover { + .dropdown-toggle-icon { + path { + stroke: white; } } } &.network-label { background-color: $dropdown-menu-background-dark; - .dropdown-toggle-icon{ - path{ + .dropdown-toggle-icon { + path { stroke: #06dd76; } } - &:hover{ - .dropdown-toggle-icon{ - path{ + &:hover { + .dropdown-toggle-icon { + path { stroke: #06dd76; } } @@ -266,8 +340,8 @@ body{ } svg { - &[data-icon="token"], - &[data-icon="user"] { + &[data-icon='token'], + &[data-icon='user'] { path { fill: transparent; stroke: $dark-text-content-color; @@ -289,8 +363,8 @@ body{ fill: #ffffff; } svg { - &[data-icon="token"], - &[data-icon="user"] { + &[data-icon='token'], + &[data-icon='user'] { path { fill: transparent; stroke: #ffffff; @@ -472,7 +546,7 @@ body{ background-color: rgba(255, 255, 255, 0.05); color: $labels-dark; - &:not([class^="tile tile-type"]) { + &:not([class^='tile tile-type']) { border-left-color: rgba(255, 255, 255, 0.15); } @@ -619,9 +693,9 @@ body{ .dropdown-item { color: #fff; - &[target='_blank']{ + &[target='_blank'] { padding-right: 20px; - &:after{ + &:after { content: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M13 8.5L9 8.5C7.89543 8.5 7 9.39543 7 10.5L7 14' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M12 6L14.5 8.5L12 11' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); } } @@ -640,16 +714,16 @@ body{ background-color: $dark-light !important; } - .btn-dropdown-line { - background-color: $dark-light; - border-color: $dark-light; - color: #ffffff; - border-radius: 8px; - } + .btn-dropdown-line { + background-color: $dark-light; + border-color: $dark-light; + color: #ffffff; + border-radius: 8px; + } .index-label { - background: rgba(255,255,255, .3); - &:after{ - border-left-color: rgba(255,255,255, .3); + background: rgba(255, 255, 255, 0.3); + &:after { + border-left-color: rgba(255, 255, 255, 0.3); } } @@ -773,22 +847,22 @@ body{ } .tooltip-pale-color.bs-tooltip-top .arrow::before, - .tooltip-pale-color.bs-tooltip-auto[x-placement^="top"] .arrow::before { + .tooltip-pale-color.bs-tooltip-auto[x-placement^='top'] .arrow::before { border-top-color: rgba(#fff, 0.5) !important; } .tooltip-pale-color.bs-tooltip-right .arrow::before, - .tooltip-pale-color.bs-tooltip-auto[x-placement^="right"] .arrow::before { + .tooltip-pale-color.bs-tooltip-auto[x-placement^='right'] .arrow::before { border-right-color: rgba(#fff, 0.5) !important; } .tooltip-pale-color.bs-tooltip-bottom .arrow::before, - .tooltip-pale-color.bs-tooltip-auto[x-placement^="bottom"] .arrow::before { + .tooltip-pale-color.bs-tooltip-auto[x-placement^='bottom'] .arrow::before { border-bottom-color: rgba(#fff, 0.5) !important; } .tooltip-pale-color.bs-tooltip-left .arrow::before, - .tooltip-pale-color.bs-tooltip-auto[x-placement^="left"] .arrow::before { + .tooltip-pale-color.bs-tooltip-auto[x-placement^='left'] .arrow::before { border-left-color: rgba(#fff, 0.5) !important; } @@ -812,7 +886,7 @@ body{ } } - .token-balance-dropdown[aria-labelledby="dropdown-tokens"] + .token-balance-dropdown[aria-labelledby='dropdown-tokens'] .dropdown-items .dropdown-item:hover { color: #fff !important; @@ -827,7 +901,7 @@ body{ } // coin balance history chart - .chartjs-render-monitor[data-chart="coinBalanceHistoryChart"] { + .chartjs-render-monitor[data-chart='coinBalanceHistoryChart'] { filter: brightness(0) invert(1) !important; } @@ -975,11 +1049,11 @@ body{ border-color: $labels-dark; } - .radio-big input[type="radio"]:checked + .radio-icon { + .radio-big input[type='radio']:checked + .radio-icon { border-color: $dark-primary; } - .radio-big input[type="radio"]:checked + .radio-icon::before { + .radio-big input[type='radio']:checked + .radio-icon::before { background: $dark-primary; } @@ -987,7 +1061,7 @@ body{ color: #fff; } - .check input[type="checkbox"]:checked + .check-icon::before { + .check input[type='checkbox']:checked + .check-icon::before { background: $dark-primary; } @@ -1440,23 +1514,23 @@ body{ } .tooltip-inversed-color.bs-tooltip-top .arrow::before, - .tooltip-inversed-color.bs-tooltip-auto[x-placement^="top"] .arrow::before { + .tooltip-inversed-color.bs-tooltip-auto[x-placement^='top'] .arrow::before { border-top-color: $btn-line-color !important; } .tooltip-inversed-color.bs-tooltip-right .arrow::before, - .tooltip-inversed-color.bs-tooltip-auto[x-placement^="right"] .arrow::before { + .tooltip-inversed-color.bs-tooltip-auto[x-placement^='right'] .arrow::before { border-right-color: $btn-line-color !important; } .tooltip-inversed-color.bs-tooltip-bottom .arrow::before, - .tooltip-inversed-color.bs-tooltip-auto[x-placement^="bottom"] + .tooltip-inversed-color.bs-tooltip-auto[x-placement^='bottom'] .arrow::before { border-bottom-color: $btn-line-color !important; } .tooltip-inversed-color.bs-tooltip-left .arrow::before, - .tooltip-inversed-color.bs-tooltip-auto[x-placement^="left"] .arrow::before { + .tooltip-inversed-color.bs-tooltip-auto[x-placement^='left'] .arrow::before { border-left-color: $btn-line-color !important; } @@ -1482,7 +1556,7 @@ body{ .main-search-autocomplete { background-color: transparent !important; color: #fff !important; - @include textfield-placeholder(rgba(255,255,255,.5)); + @include textfield-placeholder(rgba(255, 255, 255, 0.5)); } input::-webkit-input-placeholder { @@ -1503,50 +1577,51 @@ body{ color: #fff !important; } - ul[id^="autoComplete_list_"] { + ul[id^='autoComplete_list_'] { background-color: $dropdown-menu-background-dark; - .result-header{ + .result-header { background-color: $dropdown-menu-background-dark; - border-bottom-color:#4F8F8B; - .result-header-content{ - .list-toggle{ - border-color: #65B3AE; - color: #BFF6F2; - &.active,&:hover{ - background-color: #65B3AE; - color: #070D0C; - border-color: #65B3AE; + border-bottom-color: #4f8f8b; + .result-header-content { + .list-toggle { + border-color: #65b3ae; + color: #bff6f2; + &.active, + &:hover { + background-color: #65b3ae; + color: #070d0c; + border-color: #65b3ae; } } } } - .result-counter{ + .result-counter { color: $dark-text-content-color; - border-bottom-color:#4F8F8B; - .counter-content{ - p{ - &.label{ - color: #BFF6F2; + border-bottom-color: #4f8f8b; + .counter-content { + p { + &.label { + color: #bff6f2; } - &.count{ - color: #7FD8D2; + &.count { + color: #7fd8d2; } } - } + } } } - li[id^="autoComplete_result_"] { + li[id^='autoComplete_result_'] { background-color: transparent !important; color: #fff; - .result-match{ - & > div{ - div:first-child{ + .result-match { + & > div { + div:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - color: #BFF6F2; + color: #bff6f2; } } } @@ -1554,7 +1629,7 @@ body{ background-color: rgba(48, 90, 87, 0.5) !important; } - &[aria-selected="true"] { + &[aria-selected='true'] { background-color: rgba(48, 90, 87, 0.5) !important; } } diff --git a/apps/block_scout_web/assets/js/lib/custom_components.js b/apps/block_scout_web/assets/js/lib/custom_components.js new file mode 100644 index 000000000000..cebfa8c19478 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/custom_components.js @@ -0,0 +1,3 @@ +import $ from 'jquery' + +$(function () {}) diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js b/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js index cf6902f55263..3c2bae89c31e 100644 --- a/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js +++ b/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js @@ -7,7 +7,7 @@ export const disconnectSelector = '[disconnect-wallet]' const connectToSelector = '[connect-to]' const connectedToSelector = '[connected-to]' -export function getContractABI ($form) { +export function getContractABI($form) { const implementationAbi = $form.data('implementation-abi') const parentAbi = $form.data('contract-abi') const $parent = $('[data-smart-contract-functions]') @@ -16,45 +16,73 @@ export function getContractABI ($form) { return contractAbi } -export function getMethodInputs (contractAbi, functionName) { - const functionAbi = contractAbi.find(abi => - abi.name === functionName - ) +export function getMethodInputs(contractAbi, functionName) { + const functionAbi = contractAbi.find((abi) => abi.name === functionName) return functionAbi && functionAbi.inputs } -export function prepareMethodArgs ($functionInputs, inputs) { +export function prepareMethodArgs($functionInputs, inputs) { return $.map($functionInputs, (element, ind) => { const inputValue = $(element).val() const inputType = inputs[ind] && inputs[ind].type const inputComponents = inputs[ind] && inputs[ind].components let sanitizedInputValue - sanitizedInputValue = replaceDoubleQuotes(inputValue, inputType, inputComponents) - sanitizedInputValue = replaceSpaces(sanitizedInputValue, inputType, inputComponents) + sanitizedInputValue = replaceDoubleQuotes( + inputValue, + inputType, + inputComponents + ) + sanitizedInputValue = replaceSpaces( + sanitizedInputValue, + inputType, + inputComponents + ) if (isArrayInputType(inputType) || isTupleInputType(inputType)) { if (sanitizedInputValue === '' || sanitizedInputValue === '[]') { return [[]] } else { if (isArrayOfTuple(inputType)) { - const sanitizedInputValueElements = JSON.parse(sanitizedInputValue).map((elementValue, index) => { - return sanitizeMutipleInputValues(elementValue, inputType, inputComponents) + const sanitizedInputValueElements = JSON.parse( + sanitizedInputValue + ).map((elementValue, index) => { + return sanitizeMutipleInputValues( + elementValue, + inputType, + inputComponents + ) }) return [sanitizedInputValueElements] } else { - if (sanitizedInputValue.startsWith('[') && sanitizedInputValue.endsWith(']')) { - sanitizedInputValue = sanitizedInputValue.substring(1, sanitizedInputValue.length - 1) + if ( + sanitizedInputValue.startsWith('[') && + sanitizedInputValue.endsWith(']') + ) { + sanitizedInputValue = sanitizedInputValue.substring( + 1, + sanitizedInputValue.length - 1 + ) } const inputValueElements = sanitizedInputValue.split(',') - const sanitizedInputValueElements = sanitizeMutipleInputValues(inputValueElements, inputType, inputComponents) + const sanitizedInputValueElements = sanitizeMutipleInputValues( + inputValueElements, + inputType, + inputComponents + ) return [sanitizedInputValueElements] } } - } else { return convertToBool(sanitizedInputValue, inputType) } + } else { + return convertToBool(sanitizedInputValue, inputType) + } }) } -function sanitizeMutipleInputValues (inputValueElements, inputType, inputComponents) { +function sanitizeMutipleInputValues( + inputValueElements, + inputType, + inputComponents +) { return inputValueElements.map((elementValue, index) => { let elementInputType if (inputType.includes('tuple')) { @@ -63,19 +91,29 @@ function sanitizeMutipleInputValues (inputValueElements, inputType, inputCompone elementInputType = inputType.split('[')[0] } - let sanitizedElementValue = replaceDoubleQuotes(elementValue, elementInputType) - sanitizedElementValue = replaceSpaces(sanitizedElementValue, elementInputType) - sanitizedElementValue = convertToBool(sanitizedElementValue, elementInputType) + let sanitizedElementValue = replaceDoubleQuotes( + elementValue, + elementInputType + ) + sanitizedElementValue = replaceSpaces( + sanitizedElementValue, + elementInputType + ) + sanitizedElementValue = convertToBool( + sanitizedElementValue, + elementInputType + ) return sanitizedElementValue }) } -export function compareChainIDs (explorerChainId, walletChainIdHex) { - console.log("explorer chain id:", explorerChainId) - console.log("wallet chain id:", walletChainIdHex) +export function compareChainIDs(explorerChainId, walletChainIdHex) { + console.log('explorer chain id:', explorerChainId) + console.log('wallet chain id:', walletChainIdHex) if (explorerChainId !== parseInt(walletChainIdHex)) { - const networkDisplayNameFromWallet = props.getNetworkDisplayName(walletChainIdHex) + const networkDisplayNameFromWallet = + props.getNetworkDisplayName(walletChainIdHex) const networkDisplayName = props.getNetworkDisplayName(explorerChainId) const errorMsg = `You connected to ${networkDisplayNameFromWallet} chain in the wallet, but the current instance of Blockscout is for ${networkDisplayName} chain` return Promise.reject(new Error(errorMsg)) @@ -86,18 +124,30 @@ export function compareChainIDs (explorerChainId, walletChainIdHex) { export const formatError = (error) => { let { message } = error - message = message && message.split('Error: ').length > 1 ? message.split('Error: ')[1] : message + message = + message && message.split('Error: ').length > 1 + ? message.split('Error: ')[1] + : message return message } export const formatTitleAndError = (error) => { let { message } = error - let title = message && message.split('Error: ').length > 1 ? message.split('Error: ')[1] : message - title = title && title.split('{').length > 1 ? title.split('{')[0].replace(':', '') : title + let title = + message && message.split('Error: ').length > 1 + ? message.split('Error: ')[1] + : message + title = + title && title.split('{').length > 1 + ? title.split('{')[0].replace(':', '') + : title let txHash = '' let errorMap = '' try { - errorMap = message && message.indexOf('{') >= 0 ? JSON.parse(message && message.slice(message.indexOf('{'))) : '' + errorMap = + message && message.indexOf('{') >= 0 + ? JSON.parse(message && message.slice(message.indexOf('{'))) + : '' message = errorMap.error || '' txHash = errorMap.transactionHash || '' } catch (exception) { @@ -110,14 +160,14 @@ export const getCurrentAccountPromise = (provider) => { return new Promise((resolve, reject) => { if (provider && provider.wc) { getCurrentAccountFromWCPromise(provider) - .then(account => resolve(account)) - .catch(err => { + .then((account) => resolve(account)) + .catch((err) => { reject(err) }) } else { getCurrentAccountFromMMPromise() - .then(account => resolve(account)) - .catch(err => { + .then((account) => resolve(account)) + .catch((err) => { reject(err) }) } @@ -126,16 +176,17 @@ export const getCurrentAccountPromise = (provider) => { export const getCurrentAccountFromWCPromise = (provider) => { return new Promise((resolve, reject) => { - // Get a Web3 instance for the wallet + // Get a Web3 instance for the wallet const web3 = new Web3(provider) // Get list of accounts of the connected wallet - web3.eth.getAccounts() - .then(accounts => { + web3.eth + .getAccounts() + .then((accounts) => { // MetaMask does not give you all accounts, only the selected account resolve(accounts[0]) }) - .catch(err => { + .catch((err) => { reject(err) }) }) @@ -143,51 +194,64 @@ export const getCurrentAccountFromWCPromise = (provider) => { export const getCurrentAccountFromMMPromise = () => { return new Promise((resolve, reject) => { - window.ethereum.request({ method: 'eth_accounts' }) - .then(accounts => { + window.ethereum + .request({ method: 'eth_accounts' }) + .then((accounts) => { const account = accounts[0] ? accounts[0].toLowerCase() : null resolve(account) }) - .catch(err => { + .catch((err) => { reject(err) }) }) } -function hideConnectedToContainer () { - document.querySelector(connectedToSelector) && document.querySelector(connectedToSelector).classList.add('hidden') +function hideConnectedToContainer() { + document.querySelector(connectedToSelector) && + document.querySelector(connectedToSelector).classList.add('hidden') } -function showConnectedToContainer () { - document.querySelector(connectedToSelector) && document.querySelector(connectedToSelector).classList.remove('hidden') +function showConnectedToContainer() { + document.querySelector(connectedToSelector) && + document.querySelector(connectedToSelector).classList.remove('hidden') } -function hideConnectContainer () { - document.querySelector(connectSelector) && document.querySelector(connectSelector).classList.add('hidden') +function hideConnectContainer() { + document.querySelector(connectSelector) && + document.querySelector(connectSelector).classList.add('hidden') } -function showConnectContainer () { - document.querySelector(connectSelector) && document.querySelector(connectSelector).classList.remove('hidden') +function showConnectContainer() { + document.querySelector(connectSelector) && + document.querySelector(connectSelector).classList.remove('hidden') } -function hideConnectToContainer () { - document.querySelector(connectToSelector) && document.querySelector(connectToSelector).classList.add('hidden') +function hideConnectToContainer() { + document.querySelector(connectToSelector) && + document.querySelector(connectToSelector).classList.add('hidden') } -function showConnectToContainer () { - document.querySelector(connectToSelector) && document.querySelector(connectToSelector).classList.remove('hidden') +function showConnectToContainer() { + document.querySelector(connectToSelector) && + document.querySelector(connectToSelector).classList.remove('hidden') } -export function showHideDisconnectButton () { +export function showHideDisconnectButton() { // Show disconnect button only in case of Wallet Connect - if (window.web3 && window.web3.currentProvider && window.web3.currentProvider.wc) { - document.querySelector(disconnectSelector) && document.querySelector(disconnectSelector).classList.remove('hidden') + if ( + window.web3 && + window.web3.currentProvider && + window.web3.currentProvider.wc + ) { + document.querySelector(disconnectSelector) && + document.querySelector(disconnectSelector).classList.remove('hidden') } else { - document.querySelector(disconnectSelector) && document.querySelector(disconnectSelector).classList.add('hidden') + document.querySelector(disconnectSelector) && + document.querySelector(disconnectSelector).classList.add('hidden') } } -export function showConnectedToElements (account) { +export function showConnectedToElements(account) { hideConnectToContainer() showConnectContainer() showConnectedToContainer() @@ -195,35 +259,40 @@ export function showConnectedToElements (account) { setConnectToAddress(account) } -export function showConnectElements () { +export function showConnectElements() { showConnectToContainer() showConnectContainer() hideConnectedToContainer() } -export function hideConnectButton () { +export function hideConnectButton() { showConnectToContainer() hideConnectContainer() hideConnectedToContainer() } -function setConnectToAddress (account) { +function setConnectToAddress(account) { if (document.querySelector('[connected-to-address]')) { - document.querySelector('[connected-to-address]').innerHTML = `${trimmedAddressHash(account)}` + document.querySelector( + '[connected-to-address]' + ).innerHTML = `[${trimmedAddressHash( + account + )}]` } } -function trimmedAddressHash (account) { - if ($(window).width() < 544) { - return `${account.slice(0, 7)}–${account.slice(-6)}` - } else { - return account - } +function trimmedAddressHash(account) { + return `${account.slice(0, 7)}...${account.slice(-6)}` + // if ($(window).width() < 544) { + // return `${account.slice(0, 7)}–${account.slice(-6)}` + // } else { + // return account + // } } -function convertToBool (value, type) { +function convertToBool(value, type) { if (isBoolInputType(type)) { - const boolVal = (value === 'true' || value === '1' || value === 1) + const boolVal = value === 'true' || value === '1' || value === 1 return boolVal } else { @@ -231,50 +300,64 @@ function convertToBool (value, type) { } } -function isArrayInputType (inputType) { +function isArrayInputType(inputType) { return inputType && inputType.includes('[') && inputType.includes(']') } -function isTupleInputType (inputType) { - return inputType && inputType.includes('tuple') && !isArrayInputType(inputType) +function isTupleInputType(inputType) { + return ( + inputType && inputType.includes('tuple') && !isArrayInputType(inputType) + ) } -function isArrayOfTuple (inputType) { +function isArrayOfTuple(inputType) { return inputType && inputType.includes('tuple') && isArrayInputType(inputType) } -function isAddressInputType (inputType) { - return inputType && inputType.includes('address') && !isArrayInputType(inputType) +function isAddressInputType(inputType) { + return ( + inputType && inputType.includes('address') && !isArrayInputType(inputType) + ) } -function isUintInputType (inputType) { +function isUintInputType(inputType) { return inputType && inputType.includes('int') && !isArrayInputType(inputType) } -function isStringInputType (inputType) { - return inputType && inputType.includes('string') && !isArrayInputType(inputType) +function isStringInputType(inputType) { + return ( + inputType && inputType.includes('string') && !isArrayInputType(inputType) + ) } -function isBytesInputType (inputType) { - return inputType && inputType.includes('bytes') && !isArrayInputType(inputType) +function isBytesInputType(inputType) { + return ( + inputType && inputType.includes('bytes') && !isArrayInputType(inputType) + ) } -function isBoolInputType (inputType) { +function isBoolInputType(inputType) { return inputType && inputType.includes('bool') && !isArrayInputType(inputType) } -function isNonSpaceInputType (inputType) { - return isAddressInputType(inputType) || isBytesInputType(inputType) || inputType.includes('int') || inputType.includes('bool') +function isNonSpaceInputType(inputType) { + return ( + isAddressInputType(inputType) || + isBytesInputType(inputType) || + inputType.includes('int') || + inputType.includes('bool') + ) } -function replaceSpaces (value, type, components) { +function replaceSpaces(value, type, components) { if (isNonSpaceInputType(type) && isFunction(value.replace)) { return value.replace(/\s/g, '') } else if (isTupleInputType(type) && isFunction(value.split)) { return value .split(',') .map((itemValue, itemIndex) => { - const itemType = components && components[itemIndex] && components[itemIndex].type + const itemType = + components && components[itemIndex] && components[itemIndex].type return replaceSpaces(itemValue, itemType) }) @@ -287,8 +370,13 @@ function replaceSpaces (value, type, components) { } } -function replaceDoubleQuotes (value, type, components) { - if (isAddressInputType(type) || isUintInputType(type) || isStringInputType(type) || isBytesInputType(type)) { +function replaceDoubleQuotes(value, type, components) { + if ( + isAddressInputType(type) || + isUintInputType(type) || + isStringInputType(type) || + isBytesInputType(type) + ) { if (isFunction(value.replaceAll)) { return value.replaceAll('"', '') } else if (isFunction(value.replace)) { @@ -299,7 +387,8 @@ function replaceDoubleQuotes (value, type, components) { return value .split(',') .map((itemValue, itemIndex) => { - const itemType = components && components[itemIndex] && components[itemIndex].type + const itemType = + components && components[itemIndex] && components[itemIndex].type return replaceDoubleQuotes(itemValue, itemType) }) @@ -309,6 +398,6 @@ function replaceDoubleQuotes (value, type, components) { } } -function isFunction (param) { +function isFunction(param) { return typeof param === 'function' } diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/functions.js b/apps/block_scout_web/assets/js/lib/smart_contract/functions.js index 2671cdbf8628..7270ab4c58d2 100644 --- a/apps/block_scout_web/assets/js/lib/smart_contract/functions.js +++ b/apps/block_scout_web/assets/js/lib/smart_contract/functions.js @@ -1,7 +1,19 @@ import $ from 'jquery' -import { connectSelector, disconnectSelector, getCurrentAccountPromise, getContractABI, getMethodInputs, prepareMethodArgs } from './common_helpers' +import { + connectSelector, + disconnectSelector, + getCurrentAccountPromise, + getContractABI, + getMethodInputs, + prepareMethodArgs +} from './common_helpers' import { queryMethod, callMethod } from './interact' -import { walletEnabled, connectToWallet, disconnectWallet, web3ModalInit } from './connect.js' +import { + walletEnabled, + connectToWallet, + disconnectWallet, + web3ModalInit +} from './connect.js' import '../../pages/address' const loadFunctions = (element, isCustomABI, from) => { @@ -14,16 +26,24 @@ const loadFunctions = (element, isCustomABI, from) => { $.get( url, { hash, type, action, is_custom_abi: isCustomABI, from }, - response => $element.html(response) + (response) => $element.html(response) ) .done(function () { - document.querySelector(connectSelector) && document.querySelector(connectSelector).addEventListener('click', connectToWallet) - document.querySelector(disconnectSelector) && document.querySelector(disconnectSelector).addEventListener('click', disconnectWallet) + document.querySelector(connectSelector) && + document + .querySelector(connectSelector) + .addEventListener('click', connectToWallet) + document.querySelector(disconnectSelector) && + document + .querySelector(disconnectSelector) + .addEventListener('click', disconnectWallet) web3ModalInit(connectToWallet) - const selector = isCustomABI ? '[data-function-custom]' : '[data-function]' + const selector = isCustomABI + ? '[data-function-custom]' + : '[data-function]' - $(selector).each((_, element) => { + $(selector, $element).each((_, element) => { readWriteFunction(element) }) @@ -35,7 +55,11 @@ const loadFunctions = (element, isCustomABI, from) => { } else { power = parseInt($(event.currentTarget).data('power'), 10) } - const $input = $(event.currentTarget).parent().parent().parent().find('[name=function_input]') + const $input = $(event.currentTarget) + .parent() + .parent() + .parent() + .find('[name=function_input]') const currentInputVal = parseInt($input.val(), 10) || 1 const newInputVal = (currentInputVal * Math.pow(10, power)).toString() $input.val(newInputVal.toString()) @@ -83,12 +107,30 @@ const readWriteFunction = (element) => { const type = $('[data-smart-contract-functions]').data('type') const isCustomABI = $form.data('custom-abi') - walletEnabled() - .then((isWalletEnabled) => queryMethod(isWalletEnabled, url, $methodId, args, type, functionName, $responseContainer, isCustomABI)) + walletEnabled().then((isWalletEnabled) => + queryMethod( + isWalletEnabled, + url, + $methodId, + args, + type, + functionName, + $responseContainer, + isCustomABI + ) + ) } else if (action === 'write') { const explorerChainId = $form.data('chainId') - walletEnabled() - .then((isWalletEnabled) => callMethod(isWalletEnabled, $functionInputs, explorerChainId, $form, functionName, $element)) + walletEnabled().then((isWalletEnabled) => + callMethod( + isWalletEnabled, + $functionInputs, + explorerChainId, + $form, + functionName, + $element + ) + ) } }) } @@ -96,19 +138,26 @@ const readWriteFunction = (element) => { const container = $('[data-smart-contract-functions]') if (container.length) { - getWalletAndLoadFunctions(false, container) + container.each(function (_, ele) { + getWalletAndLoadFunctions(false, $(ele)) + }) } const customABIContainer = $('[data-smart-contract-functions-custom]') if (customABIContainer.length) { - getWalletAndLoadFunctions(true, customABIContainer) + customABIContainer.each(function (_, ele) { + getWalletAndLoadFunctions(true, $(ele)) + }) } -function getWalletAndLoadFunctions (isCustomABI, container) { - getCurrentAccountPromise(window.web3 && window.web3.currentProvider).then((currentAccount) => { - loadFunctions(container, isCustomABI, currentAccount) - }, () => { - loadFunctions(container, isCustomABI, null) - }) +function getWalletAndLoadFunctions(isCustomABI, container) { + getCurrentAccountPromise(window.web3 && window.web3.currentProvider).then( + (currentAccount) => { + loadFunctions(container, isCustomABI, currentAccount) + }, + () => { + loadFunctions(container, isCustomABI, null) + } + ) } diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex index de71fd226fb6..b93cc9f7ad8d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex @@ -66,11 +66,12 @@ to: AccessHelpers.get_path(@conn, :address_validation_path, :index, @address.hash) ) %> <% end %> + <%= if contract?(@address) do %> <%= link( to: AccessHelpers.get_path(@conn, :address_contract_path, :index, @address.hash), class: "card-tab #{tab_status("contracts", @conn.request_path)}") do %> - <%= gettext("Code") %> + <%= gettext("Contract") %> <%= if smart_contract_verified?(@address) do %> <%= cond do %> <% Enum.member?(dark_forest_addresses_list, current_address) -> %> @@ -91,32 +92,6 @@ <% end %> <% end %> - <%= if smart_contract_with_read_only_functions?(@address) || has_address_custom_abi_with_read_functions?(@conn, @address.hash) do %> - <%= link( - gettext("Read Contract"), - to: AccessHelpers.get_path(@conn, :address_read_contract_path, :index, @address.hash), - class: "card-tab #{tab_status("read-contract", @conn.request_path)}") - %> - <% end %> - <%= if @is_proxy do %> - <%= link( - gettext("Read Proxy"), - to: AccessHelpers.get_path(@conn, :address_read_proxy_path, :index, @address.hash), - class: "card-tab #{tab_status("read-proxy", @conn.request_path)}") - %> - <% end %> - <%= if smart_contract_with_write_functions?(@address) || has_address_custom_abi_with_write_functions?(@conn, @address.hash) do %> - <%= link( - gettext("Write Contract"), - to: AccessHelpers.get_path(@conn, :address_write_contract_path, :index, @address.hash), - class: "card-tab #{tab_status("write-contract", @conn.request_path)}") - %> - <% end %> - <%= if smart_contract_with_write_functions?(@address) && @is_proxy do %> - <%= link( - gettext("Write Proxy"), - to: AccessHelpers.get_path(@conn, :address_write_proxy_path, :index, @address.hash), - class: "card-tab #{tab_status("write-proxy", @conn.request_path)}") - %> - <% end %> + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/_code.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/_code.html.eex new file mode 100644 index 000000000000..75c28d6a1291 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/_code.html.eex @@ -0,0 +1,245 @@ +<% contract_creation_code = contract_creation_code(@address) %> +<% minimal_proxy_template = Chain.get_minimal_proxy_template(@address.hash) %> +<% metadata_for_verification = minimal_proxy_template || Chain.get_address_verified_twin_contract(@address.hash).verified_contract %> +<% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %> +<% additional_sources_from_twin = Chain.get_address_verified_twin_contract(@address.hash).additional_sources %> +<% fully_verified = Chain.smart_contract_fully_verified?(@address.hash)%> +<% additional_sources = if smart_contract_verified, do: @address.smart_contract_additional_sources, else: additional_sources_from_twin %> +<% visualize_sol2uml_enabled = Explorer.Visualize.Sol2uml.enabled?() %> + +<%= unless smart_contract_verified do %> + <%= if minimal_proxy_template do %> + <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %> + <% else %> + <%= if metadata_for_verification do %> + <% path = address_verify_contract_path(@conn, :new, @address.hash) %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> + <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link( + metadata_for_verification.address_hash, + to: address_contract_path(@conn, :index, metadata_for_verification.address_hash)) %>.
    <%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <%= gettext("Verify & Publish") %> <%= gettext("button") %>
    +
    + <%= link(gettext("Verify & Publish"), to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish") %> +
    + <% end %> + <% end %> + <% end %> + <%= if smart_contract_verified && @address.smart_contract.is_changed_bytecode do %> + <%= render BlockScoutWeb.CommonComponentsView, "_changed_bytecode_warning.html" %> + <% end %> + <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> + <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> + <%= if @address.smart_contract.partially_verified && smart_contract_verified do %> +
    + <%= gettext("This contract has been partially verified via Sourcify.") %> + <% else %> + <%= if @address.smart_contract.verified_via_sourcify && smart_contract_verified do %> +
    + <%= gettext("This contract has been verified via Sourcify.") %> + <% end %> + <% end %> + <%= if @address.smart_contract.verified_via_sourcify && smart_contract_verified do %> + target="_blank"> + View contract in Sourcify repository <%= render BlockScoutWeb.IconsView, "_external_link.html" %> + +
    + + <% end %> +
    +
    +
    <%= gettext "Contract name:" %>
    +
    <%= target_contract.name %>
    +


    +


    +
    <%= gettext "Optimization enabled" %>
    +
    <%= if target_contract.is_vyper_contract, do: "N/A", else: format_optimization_text(target_contract.optimization) %>
    +
    +
    +
    <%= gettext "Compiler version" %>
    +
    <%= target_contract.compiler_version %>
    +


    +


    + <%= if target_contract.optimization && target_contract.optimization_runs do %> +
    <%= gettext "Optimization runs" %>
    +
    <%= target_contract.optimization_runs %>
    + <% end %> +
    +
    + <%= if smart_contract_verified && target_contract.evm_version do %> +
    <%= gettext "EVM Version" %>
    +
    <%= target_contract.evm_version %>
    +


    +


    + <% end %> + <%= if target_contract.inserted_at do %> +
    <%= gettext "Verified at" %>
    +
    <%= target_contract.inserted_at %>
    + <% end %> +
    +
    + <%= if smart_contract_verified && target_contract.constructor_arguments do %> +
    +
    +

    <%= gettext "Constructor Arguments" %>

    +
    +
    +
    <%= raw(format_constructor_arguments(target_contract, @conn)) %>
    +                    
    +
    +
    + <% end %> +
    +
    +

    <%= target_contract.file_path || gettext "Contract source code" %>

    +
    + <%= if visualize_sol2uml_enabled && !target_contract.is_vyper_contract && !is_nil(target_contract.abi) do %> + + +
    + + Sol2uml +
    new
    +
    +
    +
    + <% end %> + +
    +
    +
    ><%= target_contract.contract_source_code %>
    +              
    + + <%= additional_sources |> Enum.with_index() |> Enum.map(fn {additional_source, index} -> %> +
    +
    +

    <%= additional_source.file_name %>

    + +
    +
    <%= additional_source.contract_source_code %>
    +                
    + <% end)%> + + <%= if !is_nil(target_contract.compiler_settings) do %> +
    +
    +

    <%= gettext "Compiler Settings" %>

    + +
    +
    +
    <%= format_smart_contract_abi(target_contract.compiler_settings) %>
    +                    
    +
    +
    + <% end %> + + <%= if !is_nil(target_contract.abi) do %> +
    +
    +

    <%= gettext "Contract ABI" %>

    + +
    +
    +
    <%= format_smart_contract_abi(target_contract.abi) %>
    +                    
    +
    +
    + <% end %> + + <% end %> +
    + <%= case contract_creation_code do %> + <% {:selfdestructed, transaction_init} -> %> +
    +

    <%= gettext "Contract Creation Code" %>

    + +
    +
    +

    <%= gettext "Contracts that self destruct in their constructors have no contract code published and cannot be verified." %>

    +

    <%= gettext "Displaying the init data provided of the creating transaction." %>

    +
    +
    +
    <%= transaction_init %>
    +
    + <% {:ok, contract_code} -> %> + <%= if creation_code(@address) do %> +
    +

    <%= gettext "Contract Creation Code" %>

    +
    + + <%= if !fully_verified do %> + <% path = address_verify_contract_path(@conn, :new, @address.hash) %> + <%= link( + gettext("Verify & Publish"), + to: path, + class: "button button-primary button-sm float-right ml-3", + "data-test": "verify_and_publish" + ) %> + <% end %> +
    +
    +
    +
    <%= creation_code(@address) %>
    +
    + <% end %> + <%= if fully_verified do %> +
    +

    <%= gettext "Deployed ByteCode" %>

    + +
    + <% else %> +
    +
    +

    <%= gettext "Deployed ByteCode" %>

    +
    +
    + + <%= if !fully_verified and !creation_code(@address) do %> + <% path = address_verify_contract_path(@conn, :new, @address.hash) %> + <%= link( + gettext("Verify & Publish"), + to: path, + class: "button button-primary button-sm float-right ml-3", + "data-test": "verify_and_publish" + ) %> + <% end %> +
    +
    + <% end %> +
    +
    <%= contract_code %>
    +
    + <% end %> +
    + + <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> + <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> + <%= if target_contract.external_libraries && target_contract.external_libraries != [] do %> +
    +
    +

    <%= gettext "External libraries" %>

    +
    +
    +
    <%= raw(format_external_libraries(target_contract.external_libraries, @conn)) %>
    +                    
    +
    +
    + <% end %> + <% end %> +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex index 63b350a958f8..e71292174d66 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex @@ -1,11 +1,3 @@ -<% contract_creation_code = contract_creation_code(@address) %> -<% minimal_proxy_template = Chain.get_minimal_proxy_template(@address.hash) %> -<% metadata_for_verification = minimal_proxy_template || Chain.get_address_verified_twin_contract(@address.hash).verified_contract %> -<% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %> -<% additional_sources_from_twin = Chain.get_address_verified_twin_contract(@address.hash).additional_sources %> -<% fully_verified = Chain.smart_contract_fully_verified?(@address.hash)%> -<% additional_sources = if smart_contract_verified, do: @address.smart_contract_additional_sources, else: additional_sources_from_twin %> -<% visualize_sol2uml_enabled = Explorer.Visualize.Sol2uml.enabled?() %>
    <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> @@ -14,242 +6,87 @@
    <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
    - <%= unless smart_contract_verified do %> - <%= if minimal_proxy_template do %> - <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %> - <% else %> - <%= if metadata_for_verification do %> - <% path = address_verify_contract_path(@conn, :new, @address.hash) %> -
    -
    - <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> - <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link( - metadata_for_verification.address_hash, - to: address_contract_path(@conn, :index, metadata_for_verification.address_hash)) %>.
    <%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <%= gettext("Verify & Publish") %> <%= gettext("button") %>
    -
    - <%= link(gettext("Verify & Publish"), to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish") %> -
    - <% end %> - <% end %> - <% end %> - <%= if smart_contract_verified && @address.smart_contract.is_changed_bytecode do %> - <%= render BlockScoutWeb.CommonComponentsView, "_changed_bytecode_warning.html" %> - <% end %> - <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> - <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> - <%= if @address.smart_contract.partially_verified && smart_contract_verified do %> -
    - <%= gettext("This contract has been partially verified via Sourcify.") %> - <% else %> - <%= if @address.smart_contract.verified_via_sourcify && smart_contract_verified do %> -
    - <%= gettext("This contract has been verified via Sourcify.") %> - <% end %> - <% end %> - <%= if @address.smart_contract.verified_via_sourcify && smart_contract_verified do %> - target="_blank"> - View contract in Sourcify repository <%= render BlockScoutWeb.IconsView, "_external_link.html" %> - + +
    + +
    +
    - <% end %> -
    -
    -
    <%= gettext "Contract name:" %>
    -
    <%= target_contract.name %>
    -


    -


    -
    <%= gettext "Optimization enabled" %>
    -
    <%= if target_contract.is_vyper_contract, do: "N/A", else: format_optimization_text(target_contract.optimization) %>
    -
    -
    -
    <%= gettext "Compiler version" %>
    -
    <%= target_contract.compiler_version %>
    -


    -


    - <%= if target_contract.optimization && target_contract.optimization_runs do %> -
    <%= gettext "Optimization runs" %>
    -
    <%= target_contract.optimization_runs %>
    - <% end %> -
    -
    - <%= if smart_contract_verified && target_contract.evm_version do %> -
    <%= gettext "EVM Version" %>
    -
    <%= target_contract.evm_version %>
    -


    -


    - <% end %> - <%= if target_contract.inserted_at do %> -
    <%= gettext "Verified at" %>
    -
    <%= target_contract.inserted_at %>
    - <% end %> -
    -
    - <%= if smart_contract_verified && target_contract.constructor_arguments do %> -
    -
    -

    <%= gettext "Constructor Arguments" %>

    -
    -
    -
    <%= raw(format_constructor_arguments(target_contract, @conn)) %>
    -              
    -
    -
    - <% end %> -
    -
    -

    <%= target_contract.file_path || gettext "Contract source code" %>

    -
    - <%= if visualize_sol2uml_enabled && !target_contract.is_vyper_contract && !is_nil(target_contract.abi) do %> - - -
    - - Sol2uml -
    new
    -
    -
    -
    - <% end %> - -
    -
    -
    ><%= target_contract.contract_source_code %>
    -        
    +
    - <%= additional_sources |> Enum.with_index() |> Enum.map(fn {additional_source, index} -> %> -
    -
    -

    <%= additional_source.file_name %>

    - -
    -
    <%= additional_source.contract_source_code %>
    -          
    - <% end)%> + <%= + for status <- ["error", "warning", "success", "question"] do + render BlockScoutWeb.CommonComponentsView, "_modal_status.html", status: status + end + %> + <%= render BlockScoutWeb.SmartContractView, "_pending_contract_write.html" %> - <%= if !is_nil(target_contract.compiler_settings) do %> -
    -
    -

    <%= gettext "Compiler Settings" %>

    - -
    -
    -
    <%= format_smart_contract_abi(target_contract.compiler_settings) %>
    -              
    -
    -
    +
    +
    + <%= render "_code.html", address: @address, conn: @conn %> +
    + + <%= if smart_contract_with_read_only_functions?(@address) || has_address_custom_abi_with_read_functions?(@conn, @address.hash) do %> +
    + <%= render BlockScoutWeb.AddressReadContractView, "_read_contract.html", address: @address, conn: @conn %> +
    <% end %> - <%= if !is_nil(target_contract.abi) do %> -
    -
    -

    <%= gettext "Contract ABI" %>

    - -
    -
    -
    <%= format_smart_contract_abi(target_contract.abi) %>
    -              
    -
    -
    + <%= if is_proxy do %> +
    read proxy
    <% end %> - <% end %> -
    - <%= case contract_creation_code do %> - <% {:selfdestructed, transaction_init} -> %> -
    -

    <%= gettext "Contract Creation Code" %>

    - -
    -
    -

    <%= gettext "Contracts that self destruct in their constructors have no contract code published and cannot be verified." %>

    -

    <%= gettext "Displaying the init data provided of the creating transaction." %>

    -
    -
    -
    <%= transaction_init %>
    -
    - <% {:ok, contract_code} -> %> - <%= if creation_code(@address) do %> -
    -

    <%= gettext "Contract Creation Code" %>

    -
    - - <%= if !fully_verified do %> - <% path = address_verify_contract_path(@conn, :new, @address.hash) %> - <%= link( - gettext("Verify & Publish"), - to: path, - class: "button button-primary button-sm float-right ml-3", - "data-test": "verify_and_publish" - ) %> - <% end %> -
    -
    -
    -
    <%= creation_code(@address) %>
    -
    - <% end %> - <%= if fully_verified do %> -
    -

    <%= gettext "Deployed ByteCode" %>

    - -
    - <% else %> -
    -
    -

    <%= gettext "Deployed ByteCode" %>

    -
    -
    - - <%= if !fully_verified and !creation_code(@address) do %> - <% path = address_verify_contract_path(@conn, :new, @address.hash) %> - <%= link( - gettext("Verify & Publish"), - to: path, - class: "button button-primary button-sm float-right ml-3", - "data-test": "verify_and_publish" - ) %> - <% end %> -
    -
    - <% end %> -
    -
    <%= contract_code %>
    + <%= if smart_contract_with_write_functions?(@address) || has_address_custom_abi_with_write_functions?(@conn, @address.hash) do %> +
    + <%= render BlockScoutWeb.AddressWriteContractView, "_write_contract.html", address: @address, conn: @conn %>
    <% end %> -
    - <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> - <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> - <%= if target_contract.external_libraries && target_contract.external_libraries != [] do %> -
    -
    -

    <%= gettext "External libraries" %>

    -
    -
    -
    <%= raw(format_external_libraries(target_contract.external_libraries, @conn)) %>
    -              
    -
    -
    + <%= if smart_contract_with_write_functions?(@address) && is_proxy do %> +
    write proxy
    <% end %> - <% end %> + +
    + + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_read_contract.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_read_contract.html.eex new file mode 100644 index 000000000000..9c52657d0107 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_read_contract.html.eex @@ -0,0 +1,50 @@ + +<% read_contract_data = initial_data(@conn, to_string(@address.hash)) %> + + + +
    + +
    + <%= render BlockScoutWeb.SmartContractView, "_connect_container.html" %> +
    + + <%= if read_contract_data[:non_custom_abi] && read_contract_data[:custom_abi] do %> + + <% else %> + <%= if read_contract_data[:custom_abi] do %> +

    <%= gettext "Custom ABI from account" %>

    + <% end %> + <% end %> + + <%= if read_contract_data[:non_custom_abi] && read_contract_data[:custom_abi] do %> +
    + <% end %> + <%= if read_contract_data[:non_custom_abi] do %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <% end %> + <%= if read_contract_data[:custom_abi] do %> + +
    " id="custom" role="tabpanel" aria-labelledby="custom-tab" data-smart-contract-functions-custom data-hash="<%= to_string(@address.hash) %>" data-type="<%= read_contract_data[:type] %>" data-action="<%= read_contract_data[:action] %>" data-url="<%= smart_contract_path(@conn, :index) %>"> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <% end %> + <%= if read_contract_data[:non_custom_abi] && read_contract_data[:custom_abi] do %> +
    + <% end %> + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_write_contract.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_write_contract.html.eex new file mode 100644 index 000000000000..5f915f08f8eb --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_write_contract.html.eex @@ -0,0 +1,47 @@ + +<% write_contract_data = initial_data(@conn, to_string(@address.hash)) %> + +
    + +
    + <%= render BlockScoutWeb.SmartContractView, "_connect_container.html" %> +
    + <%= if write_contract_data[:non_custom_abi] && write_contract_data[:custom_abi] do %> + + <% else %> + <%= if write_contract_data[:custom_abi] do %> +

    <%= gettext "Custom ABI from account" %>

    + <% end %> + <% end %> + + <%= if write_contract_data[:non_custom_abi] && write_contract_data[:custom_abi] do %> +
    + <% end %> + <%= if write_contract_data[:non_custom_abi] do %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <% end %> + <%= if write_contract_data[:custom_abi] do %> + +
    " id="custom" role="tabpanel" aria-labelledby="custom-tab" data-smart-contract-functions-custom data-hash="<%= to_string(@address.hash) %>" data-type="<%= write_contract_data[:type] %>" data-action="<%= write_contract_data[:action] %>" data-url="<%= smart_contract_path(@conn, :index) %>"> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <% end %> + <%= if write_contract_data[:non_custom_abi] && write_contract_data[:custom_abi] do %> +
    + <% end %> + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_connect_container.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_connect_container.html.eex index 2438dd23a00a..8d0982330828 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_connect_container.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_connect_container.html.eex @@ -1,16 +1,24 @@
    - - <%= render BlockScoutWeb.IconsView, "_inactive_icon.html" %> - -

    Disconnected

    - + +
    - diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/_read_proxy.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/_read_proxy.html.eex new file mode 100644 index 000000000000..eacac502c2e6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/_read_proxy.html.eex @@ -0,0 +1,15 @@ + +<% read_proxy_data = initial_data(@conn, to_string(@address.hash)) %> + +
    + +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/_write_proxy.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/_write_proxy.html.eex new file mode 100644 index 000000000000..3d93d181c5db --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/_write_proxy.html.eex @@ -0,0 +1,12 @@ + +<% write_proxy_data = initial_data(@conn, to_string(@address.hash)) %> + +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex index e51247fdd8e2..c4271d2e111b 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex @@ -1,3 +1,46 @@ defmodule BlockScoutWeb.AddressReadProxyView do use BlockScoutWeb, :view + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.AccessHelpers + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + + def initial_data(conn, address_hash_string) do + address_options = [ + necessity_by_association: %{ + :contracts_creation_internal_transaction => :optional, + :names => :optional, + :smart_contract => :optional, + :token => :optional, + :contracts_creation_transaction => :optional + } + ] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true), + false <- is_nil(address.smart_contract), + {:ok, false} <- + AccessHelpers.restricted_access?(address_hash_string, %{ + "address_id" => address_hash_string + }) do + [ + address: address, + type: :proxy, + action: :read, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: + address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ] + else + _ -> + nil + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex index 17634e9fb195..e22612f10032 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex @@ -1,3 +1,42 @@ defmodule BlockScoutWeb.AddressWriteProxyView do use BlockScoutWeb, :view + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.AccessHelpers + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + + def initial_data(conn, address_hash_string) do + address_options = [ + necessity_by_association: %{ + :contracts_creation_internal_transaction => :optional, + :names => :optional, + :smart_contract => :optional, + :token => :optional, + :contracts_creation_transaction => :optional + } + ] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true), + false <- is_nil(address.smart_contract), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, %{"address_id" => address_hash_string}) do + [ + address: address, + type: :proxy, + action: :write, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ] + else + _ -> + nil + end + end end From 2997a839dc68d0befedbf8ac120b4abdb860ea52 Mon Sep 17 00:00:00 2001 From: dennisloh95 Date: Fri, 14 Jul 2023 20:57:54 +0800 Subject: [PATCH 08/73] chore: update contract read write function ui --- .../assets/css/theme/_mantle-theme.scss | 61 +++++ .../js/lib/smart_contract/common_helpers.js | 62 +++-- .../assets/js/lib/smart_contract/connect.js | 55 ++-- .../assets/js/lib/smart_contract/functions.js | 26 +- .../block_scout_web/assets/js/pages/layout.js | 178 +++++++------ .../templates/address_contract/index.html.eex | 1 - .../_read_contract.html.eex | 8 +- .../_write_contract.html.eex | 4 +- .../smart_contract/_functions.html.eex | 238 ++++++++++-------- .../lib/explorer/smart_contract/reader.ex | 2 +- 10 files changed, 394 insertions(+), 241 deletions(-) diff --git a/apps/block_scout_web/assets/css/theme/_mantle-theme.scss b/apps/block_scout_web/assets/css/theme/_mantle-theme.scss index 15b464be13ce..0738b9197efd 100644 --- a/apps/block_scout_web/assets/css/theme/_mantle-theme.scss +++ b/apps/block_scout_web/assets/css/theme/_mantle-theme.scss @@ -19,6 +19,57 @@ body { overscroll-behavior: none; } +.contract-function { + width: 100%; + border: 1px solid #f0f1f2; + border-radius: 8px; + + .contract-function-header { + background-color: #f8f8f9; + &:hover { + background-color: #f0f1f2; + } + button { + padding: 8px; + outline: none; + border: none; + display: flex; + align-items: center; + justify-content: space-between; + color: #2b3034; + text-decoration: none; + box-shadow: none !important; + } + } + .contract-function-body { + padding: 12px; + overflow: hidden; + .form-inline { + flex-direction: column; + align-items: flex-start; + .form-group { + width: 100%; + label { + margin-bottom: 0.5rem; + } + input[name='function_input'] { + width: 100%; + border-radius: 8px; + } + } + input[type='submit'] { + border: 1px solid #cfd2d4; + background-color: #f8f8f9; + padding: 6px 16px 6px 16px !important; + color: #152928; + &:hover { + background-color: #cfd2d4; + } + } + } + } +} + .tab-content { .card-misc-container { padding: 0; @@ -284,6 +335,16 @@ body { .dark-theme-applied { color: #fff; + .tab-content { + .connect-container { + .web3-btn { + border-color: #ffffff4d; + background-color: #1f2425; + color: #ffffffcc; + } + } + } + .contract-tabs { ul { .nav-item { diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js b/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js index 3c2bae89c31e..bec1038823dc 100644 --- a/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js +++ b/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js @@ -207,33 +207,45 @@ export const getCurrentAccountFromMMPromise = () => { } function hideConnectedToContainer() { - document.querySelector(connectedToSelector) && - document.querySelector(connectedToSelector).classList.add('hidden') + $(connectedToSelector).length && + $(connectedToSelector).each((_, ele) => { + $(ele).addClass('hidden') + }) } function showConnectedToContainer() { - document.querySelector(connectedToSelector) && - document.querySelector(connectedToSelector).classList.remove('hidden') + $(connectedToSelector).length && + $(connectedToSelector).each((_, ele) => { + $(ele).removeClass('hidden') + }) } function hideConnectContainer() { - document.querySelector(connectSelector) && - document.querySelector(connectSelector).classList.add('hidden') + $(connectSelector).length && + $(connectSelector).each((_, ele) => { + $(ele).addClass('hidden') + }) } function showConnectContainer() { - document.querySelector(connectSelector) && - document.querySelector(connectSelector).classList.remove('hidden') + $(connectSelector).length && + $(connectSelector).each((_, ele) => { + $(ele).removeClass('hidden') + }) } function hideConnectToContainer() { - document.querySelector(connectToSelector) && - document.querySelector(connectToSelector).classList.add('hidden') + $(connectToSelector).length && + $(connectToSelector).each((_, ele) => { + $(ele).addClass('hidden') + }) } function showConnectToContainer() { - document.querySelector(connectToSelector) && - document.querySelector(connectToSelector).classList.remove('hidden') + $(connectToSelector).length && + $(connectToSelector).each((_, ele) => { + $(ele).removeClass('hidden') + }) } export function showHideDisconnectButton() { @@ -243,11 +255,15 @@ export function showHideDisconnectButton() { window.web3.currentProvider && window.web3.currentProvider.wc ) { - document.querySelector(disconnectSelector) && - document.querySelector(disconnectSelector).classList.remove('hidden') + $(disconnectSelector).length && + $(disconnectSelector).each((_, ele) => { + $(ele).removeClass('hidden') + }) } else { - document.querySelector(disconnectSelector) && - document.querySelector(disconnectSelector).classList.add('hidden') + $(disconnectSelector).length && + $(disconnectSelector).each((_, ele) => { + $(ele).addClass('hidden') + }) } } @@ -272,12 +288,14 @@ export function hideConnectButton() { } function setConnectToAddress(account) { - if (document.querySelector('[connected-to-address]')) { - document.querySelector( - '[connected-to-address]' - ).innerHTML = `[${trimmedAddressHash( - account - )}]` + if ($('[connected-to-address]').length) { + $('[connected-to-address]').each((_, ele) => { + $(ele).html( + `[${trimmedAddressHash( + account + )}]` + ) + }) } } diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/connect.js b/apps/block_scout_web/assets/js/lib/smart_contract/connect.js index ca896ef1957d..91c628d23605 100644 --- a/apps/block_scout_web/assets/js/lib/smart_contract/connect.js +++ b/apps/block_scout_web/assets/js/lib/smart_contract/connect.js @@ -1,7 +1,12 @@ import Web3 from 'web3' import Web3Modal from 'web3modal' import WalletConnectProvider from '@walletconnect/web3-provider' -import { compareChainIDs, formatError, showConnectElements, showConnectedToElements } from './common_helpers' +import { + compareChainIDs, + formatError, + showConnectElements, + showConnectedToElements +} from './common_helpers' import { openWarningModal } from '../modals' const instanceChainIdStr = document.getElementById('js-chain-id').value @@ -19,7 +24,7 @@ let web3Modal /** * Setup the orchestra */ -export async function web3ModalInit (connectToWallet, ...args) { +export async function web3ModalInit(connectToWallet, ...args) { return new Promise((resolve) => { // Tell Web3modal what providers we have available. // Built-in web browser provider (only one can exist as a time) @@ -47,17 +52,24 @@ export async function web3ModalInit (connectToWallet, ...args) { export const walletEnabled = () => { return new Promise((resolve) => { - if (window.web3 && window.web3.currentProvider && window.web3.currentProvider.wc) { + if ( + window.web3 && + window.web3.currentProvider && + window.web3.currentProvider.wc + ) { resolve(true) } else { if (window.ethereum) { window.web3 = new Web3(window.ethereum) - window.ethereum._metamask.isUnlocked() - .then(isUnlocked => { - if (isUnlocked && window.ethereum.isNiftyWallet) { // Nifty Wallet + window.ethereum._metamask + .isUnlocked() + .then((isUnlocked) => { + if (isUnlocked && window.ethereum.isNiftyWallet) { + // Nifty Wallet window.web3 = new Web3(window.web3.currentProvider) resolve(true) - } else if (isUnlocked === false && window.ethereum.isNiftyWallet) { // Nifty Wallet + } else if (isUnlocked === false && window.ethereum.isNiftyWallet) { + // Nifty Wallet window.ethereum.enable() resolve(false) } else { @@ -66,18 +78,19 @@ export const walletEnabled = () => { window.web3 = new Web3(window.web3.currentProvider) resolve(true) } else { - return window.ethereum.request({ method: 'eth_requestAccounts' }) + return window.ethereum + .request({ method: 'eth_requestAccounts' }) .then((_res) => { window.web3 = new Web3(window.web3.currentProvider) resolve(true) }) - .catch(_error => { + .catch((_error) => { resolve(false) }) } } }) - .catch(_error => { + .catch((_error) => { resolve(false) }) } else if (window.web3) { @@ -90,7 +103,7 @@ export const walletEnabled = () => { }) } -export async function disconnect () { +export async function disconnect() { if (provider && provider.close) { await provider.close() } @@ -109,7 +122,7 @@ export async function disconnect () { /** * Disconnect wallet button pressed. */ -export async function disconnectWallet () { +export async function disconnectWallet() { await disconnect() showConnectElements() @@ -118,13 +131,11 @@ export async function disconnectWallet () { export const connectToProvider = () => { return new Promise((resolve, reject) => { try { - web3Modal - .connect() - .then((connectedProvider) => { - provider = connectedProvider - window.web3 = new Web3(provider) - resolve(provider) - }) + web3Modal.connect().then((connectedProvider) => { + provider = connectedProvider + window.web3 = new Web3(provider) + resolve(provider) + }) } catch (e) { reject(e) } @@ -149,7 +160,7 @@ export const connectToWallet = async () => { provider.on('chainChanged', (chainId) => { compareChainIDs(instanceChainId, chainId) .then(() => fetchAccountData(showConnectedToElements, [])) - .catch(error => { + .catch((error) => { openWarningModal('Unauthorized', formatError(error)) }) }) @@ -161,14 +172,14 @@ export const connectToWallet = async () => { await fetchAccountData(showConnectedToElements, []) } -export async function fetchAccountData (setAccount, args) { +export async function fetchAccountData(setAccount, args) { // Get a Web3 instance for the wallet if (provider) { window.web3 = new Web3(provider) } // Get list of accounts of the connected wallet - const accounts = window.web3 && await window.web3.eth.getAccounts() + const accounts = window.web3 && (await window.web3.eth.getAccounts()) // MetaMask does not give you all accounts, only the selected account if (accounts && accounts.length > 0) { diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/functions.js b/apps/block_scout_web/assets/js/lib/smart_contract/functions.js index 7270ab4c58d2..01f98f425571 100644 --- a/apps/block_scout_web/assets/js/lib/smart_contract/functions.js +++ b/apps/block_scout_web/assets/js/lib/smart_contract/functions.js @@ -16,6 +16,7 @@ import { } from './connect.js' import '../../pages/address' +let walletInit = false const loadFunctions = (element, isCustomABI, from) => { const $element = $(element) const url = $element.data('url') @@ -29,15 +30,22 @@ const loadFunctions = (element, isCustomABI, from) => { (response) => $element.html(response) ) .done(function () { - document.querySelector(connectSelector) && - document - .querySelector(connectSelector) - .addEventListener('click', connectToWallet) - document.querySelector(disconnectSelector) && - document - .querySelector(disconnectSelector) - .addEventListener('click', disconnectWallet) - web3ModalInit(connectToWallet) + $(connectSelector, $(`#${action}-contract`)).length && + $(connectSelector, $(`#${action}-contract`)).on( + 'click', + connectToWallet + ) + + $(disconnectSelector, $(`#${action}-contract`)).length && + $(disconnectSelector, $(`#${action}-contract`)).on( + 'click', + disconnectWallet + ) + + if (!walletInit) { + web3ModalInit(connectToWallet) + walletInit = true + } const selector = isCustomABI ? '[data-function-custom]' diff --git a/apps/block_scout_web/assets/js/pages/layout.js b/apps/block_scout_web/assets/js/pages/layout.js index 91440700c635..4316e4cc663f 100644 --- a/apps/block_scout_web/assets/js/pages/layout.js +++ b/apps/block_scout_web/assets/js/pages/layout.js @@ -37,9 +37,15 @@ $('.save-address-button').click((_event) => { eth_incoming: $('#watchlist_address_watch_coin_input').prop('checked'), eth_outgoing: $('#watchlist_address_watch_coin_output').prop('checked'), erc_20_incoming: $('#watchlist_address_watch_erc_20_input').prop('checked'), - erc_20_outgoing: $('#watchlist_address_watch_erc_20_output').prop('checked'), - erc_721_1155_incoming: $('#watchlist_address_watch_erc_721_input').prop('checked'), - erc_721_1155_outgoing: $('#watchlist_address_watch_erc_721_output').prop('checked'), + erc_20_outgoing: $('#watchlist_address_watch_erc_20_output').prop( + 'checked' + ), + erc_721_1155_incoming: $('#watchlist_address_watch_erc_721_input').prop( + 'checked' + ), + erc_721_1155_outgoing: $('#watchlist_address_watch_erc_721_output').prop( + 'checked' + ), email_notifications: $('#watchlist_address_notify_email').prop('checked') } const eventName = 'New address to watchlist completed' @@ -94,64 +100,68 @@ $('.send-public-tag-request-button').click((_event) => { email: $('#public_tags_request_email').val(), company_name: $('#public_tags_request_company').val(), company_website: $('#public_tags_request_website').val(), - goal: $('#public_tags_request_is_owner_true').prop('checked') ? 'Add tags' : 'Incorrect public tag', + goal: $('#public_tags_request_is_owner_true').prop('checked') + ? 'Add tags' + : 'Incorrect public tag', public_tag: $('#public_tags_request_tags').val(), - smart_contracts: $('*[id=public_tags_request_addresses]').map((_i, el) => { - return el.value - }).get(), + smart_contracts: $('*[id=public_tags_request_addresses]') + .map((_i, el) => { + return el.value + }) + .get(), reason: $('#public_tags_request_additional_comment').val() } analytics.trackEvent(eventName, eventProperties) }) -$(document).ready(() => { - let timer - const waitTime = 500 - const observer = new MutationObserver((mutations) => { - if (mutations[0].target.hidden) { - return - } - - const $results = $('li[id^="autoComplete_result_"]') - - clearTimeout(timer) - timer = setTimeout(() => { - let eventName = 'Occurs searching according to substring at the nav bar' - let eventProperties = { - search: $('.main-search-autocomplete').val() || $('.main-search-autocomplete-mobile').val() - } - - analytics.trackEvent(eventName, eventProperties) - - eventName = 'Search list displays at the nav bar' - eventProperties = { - resultsNumber: $results.length, - results: $results.map((_i, el) => { - return el.children[1].innerText - }) - } - - analytics.trackEvent(eventName, eventProperties) - }, waitTime) - - $results.click((event) => { - const eventName = 'Search item click at the nav bar' - const eventProperties = { - item: event.currentTarget.innerText - } - - analytics.trackEvent(eventName, eventProperties) - }) - }) - observer.observe($('#autoComplete_list_1')[0], { - attributeFilter: ['hidden'], - childList: true - }) - observer.observe($('#autoComplete_list_2')[0], { - attributeFilter: ['hidden'] - }) -}) +// $(document).ready(() => { +// let timer +// const waitTime = 500 +// const observer = new MutationObserver((mutations) => { +// if (mutations[0].target.hidden) { +// return +// } + +// const $results = $('li[id^="autoComplete_result_"]') + +// clearTimeout(timer) +// timer = setTimeout(() => { +// let eventName = 'Occurs searching according to substring at the nav bar' +// let eventProperties = { +// search: $('.main-search-autocomplete').val() || $('.main-search-autocomplete-mobile').val() +// } + +// analytics.trackEvent(eventName, eventProperties) + +// eventName = 'Search list displays at the nav bar' +// eventProperties = { +// resultsNumber: $results.length, +// results: $results.map((_i, el) => { +// return el.children[1].innerText +// }) +// } + +// analytics.trackEvent(eventName, eventProperties) +// }, waitTime) + +// $results.click((event) => { +// const eventName = 'Search item click at the nav bar' +// const eventProperties = { +// item: event.currentTarget.innerText +// } + +// analytics.trackEvent(eventName, eventProperties) +// }) +// }) +// observer.observe($('#autoComplete_list_1')[0], { +// attributeFilter: ['hidden'], +// childList: true +// }) +// observer.observe($('#autoComplete_list_2')[0], { +// attributeFilter: ['hidden'] +// }) +// }) $(document).click(function (event) { const clickover = $(event.target) @@ -180,7 +190,7 @@ $(document) $('.main-search-autocomplete').trigger('focus') } }) - .on('click', '.js-btn-add-chain-to-mm', event => { + .on('click', '.js-btn-add-chain-to-mm', (event) => { const $btn = $(event.target) addChainToMM({ btn: $btn }) }) @@ -200,38 +210,52 @@ $('.main-search-autocomplete').on('keyup', function (event) { }) $('#search-icon').on('click', function (event) { - const value = $('.main-search-autocomplete').val() || $('.main-search-autocomplete-mobile').val() + const value = + $('.main-search-autocomplete').val() || + $('.main-search-autocomplete-mobile').val() search(value) }) $('#search-btn').on('click', function (event) { - const value = $('.main-search-autocomplete').val() || $('.main-search-autocomplete-mobile').val() + const value = + $('.main-search-autocomplete').val() || + $('.main-search-autocomplete-mobile').val() search(value) }) -if(window.innerWidth <= 1199){ - if(document.getElementsByClassName('search-btn-cls') && document.getElementsByClassName('search-btn-cls').length > 1){ - document.getElementsByClassName('search-btn-cls')[1].addEventListener('click', function (){ - const els = document.getElementsByClassName('main-search-autocomplete'); - if(els && els.length > 1){ - let value = els[1].value - if(value){ - search(value) +if (window.innerWidth <= 1199) { + if ( + document.getElementsByClassName('search-btn-cls') && + document.getElementsByClassName('search-btn-cls').length > 1 + ) { + document + .getElementsByClassName('search-btn-cls')[1] + .addEventListener('click', function () { + const els = document.getElementsByClassName('main-search-autocomplete') + if (els && els.length > 1) { + let value = els[1].value + if (value) { + search(value) + } } - } - }) + }) } } -if(window.innerWidth <= 1199){ - if(document.getElementsByClassName('search-icon-cls') && document.getElementsByClassName('search-icon-cls').length > 1){ - document.getElementsByClassName('search-icon-cls')[1].addEventListener('click', function (){ - const els = document.getElementsByClassName('main-search-autocomplete'); - if(els && els.length > 1){ - let value = els[1].value - if(value){ - search(value) +if (window.innerWidth <= 1199) { + if ( + document.getElementsByClassName('search-icon-cls') && + document.getElementsByClassName('search-icon-cls').length > 1 + ) { + document + .getElementsByClassName('search-icon-cls')[1] + .addEventListener('click', function () { + const els = document.getElementsByClassName('main-search-autocomplete') + if (els && els.length > 1) { + let value = els[1].value + if (value) { + search(value) + } } - } - }) + }) } } diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex index befd0d43f542..92d3fe05927d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex @@ -91,6 +91,5 @@
    - diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_read_contract.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_read_contract.html.eex index 9c52657d0107..d2e35fec8460 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_read_contract.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_read_contract.html.eex @@ -29,11 +29,11 @@ <% end %> <%= if read_contract_data[:non_custom_abi] do %> -
    -
    - <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    -
    <% end %> <%= if read_contract_data[:custom_abi] do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_write_contract.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_write_contract.html.eex index 5f915f08f8eb..1f5f058ce9d8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_write_contract.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_write_contract.html.eex @@ -9,7 +9,7 @@ <%= if write_contract_data[:non_custom_abi] && write_contract_data[:custom_abi] do %>