From 7e2e8e47d1b568f8c3179bb434ac59b61db57aca Mon Sep 17 00:00:00 2001 From: TranTrungTien <71311738+TranTrungTien@users.noreply.github.com> Date: Mon, 1 Jul 2024 09:17:11 +0700 Subject: [PATCH] [Feat][655] Enhance event log (#3571) * feat: Decode event log * [Feat][655] enhance event log * Apply decode ui * update: data decoded * [Feat][655] enhance event log - fix index * [Feat][655] enhance event log - update --------- Co-authored-by: tambui --- src/app/core/services/transaction.service.ts | 28 ++++ .../decode-message.component.html | 32 +++++ .../decode-message.component.scss | 56 ++++++++ .../decode-message.component.ts | 37 +++++ .../evm-message/evm-message.component.html | 23 +--- .../evm-message/evm-message.component.scss | 1 - .../evm-message/evm-message.component.ts | 127 +++++++++++------- .../evm-transaction-event-log.component.html | 33 +++-- .../evm-transaction-event-log.component.scss | 8 -- .../evm-transaction-event-log.component.ts | 14 +- .../pages/transaction/transaction.module.ts | 2 + src/assets/config/config.json | 2 +- 12 files changed, 262 insertions(+), 101 deletions(-) create mode 100644 src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.html create mode 100644 src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.scss create mode 100644 src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.ts diff --git a/src/app/core/services/transaction.service.ts b/src/app/core/services/transaction.service.ts index 93057c707..30433c56d 100644 --- a/src/app/core/services/transaction.service.ts +++ b/src/app/core/services/transaction.service.ts @@ -776,6 +776,34 @@ export class TransactionService extends CommonService { .pipe(map((res) => (res?.data ? res?.data[this.envDB] : null))); } + getListAbiContract(addressList: string[]) { + const operationsDoc = ` + query getListAbiContract($address: [String] = null) { + ${this.envDB} { + evm_contract_verification(where: {contract_address: {_in: $address}, status: {_eq: "SUCCESS"}}, order_by: {id: desc}) { + contract_address + created_at + creator_tx_hash + id + status + updated_at + abi + } + } + } + `; + + return this.http + .post(this.graphUrl, { + query: operationsDoc, + variables: { + address: addressList, + }, + operationName: 'getListAbiContract', + }) + .pipe(map((res) => (res?.data ? res?.data[this.envDB] : null))); + } + getListMappingName(methodId: any): Observable { const operationsDoc = ` query queryListNameMethod($limit: Int = 100, $methodId: [String!] = null) { diff --git a/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.html b/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.html new file mode 100644 index 000000000..e33e6fe1b --- /dev/null +++ b/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.html @@ -0,0 +1,32 @@ +
+
+

+ {{index}} : {{name}} +

+ +
+
+

{{value}}

+

+
+
+ + {{data}} + + {{data}} +
+
\ No newline at end of file diff --git a/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.scss b/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.scss new file mode 100644 index 000000000..066b2a80b --- /dev/null +++ b/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.scss @@ -0,0 +1,56 @@ +.decode-message { + .data{ + font-size: 14px; + line-height: 24px; + } + + .name { + border-radius: 6px; + padding: 4px 10px; + display: flex; + align-items: center; + justify-content: center; + color: #B4B8C0; + border: 1px solid var(--aura-gray-6); + background: var(--aura-gray-9); + font-weight: 600; + font-size: 12px; + white-space: nowrap; + margin: 0; + } + button.button { + background-color: #494C58; + padding-left: 6px; + padding-right: 25px; + font-size: 12px; + line-height: 16px; + bottom: 1px; + top: -1px; + } + + .button.button-dropdown.button--sm:before, + .button.button-dropdown.button--sm:after { + right: 5px; + } + + .dropdown-menu.aura-dropdown-menu{ + top: 0px; + margin-top: 0px; + } + + .dropdown-item.cursor-pointer { + padding: 0 4px; + margin-top: 2px; + + button{ + background-color: transparent; + } + } + + .highlight { + max-width: 98%; + padding: 4px 8px; + border-radius: 4px; + background-color: var(--aura-gray-10); + } +} \ No newline at end of file diff --git a/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.ts b/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.ts new file mode 100644 index 000000000..667092b58 --- /dev/null +++ b/src/app/pages/transaction/evm-transaction/decode-message/decode-message.component.ts @@ -0,0 +1,37 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { EnvironmentService } from 'src/app/core/data-services/environment.service'; + +@Component({ + selector: 'app-decode-message', + templateUrl: './decode-message.component.html', + styleUrls: ['./decode-message.component.scss'], +}) +export class DecodeMessageComponent implements OnInit { + @Input() index: number | string; + @Input() isLink?: boolean; + @Input() name?: string; + @Input() isAllowSwitchDecode?: boolean; + @Input() value: string; + @Input() decode: string; + @Input() isHighlight?: boolean; + + data = ''; + type: 'Decode' | 'Hex' = 'Hex'; + isMobile = false; + + constructor(private environmentService: EnvironmentService) {} + + ngOnInit(): void { + this.isMobile = this.environmentService.isMobile; + this.data = this.value; + } + onDecode() { + this.type = 'Decode'; + this.data = this.decode; + } + + onHex() { + this.type = 'Hex'; + this.data = this.value; + } +} diff --git a/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.html b/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.html index 19e98e1b8..b2d965907 100644 --- a/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.html +++ b/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.html @@ -1,16 +1,9 @@
-
-
- {{ 'Method' }} -
-
{{ method }}
-
-
Contract Address
-
+
-
-
- {{ 'Input Data' }} -
-
+
+ *ngFor="let topic of topicsDecoded; let i = index" + [eventLog]="eventLog[i]" + [topicsDecoded]="topic" + [index]="i">
diff --git a/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.scss b/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.scss index 5f275cbbb..8c6fa4239 100644 --- a/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.scss +++ b/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.scss @@ -16,7 +16,6 @@ border-radius: 8px; padding: 12px; max-height: 558px; - overflow: auto; @media (min-width: 992px) { max-height: 330px; padding: 12px 16px; diff --git a/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.ts b/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.ts index 06a11af9a..65377f0dc 100644 --- a/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.ts +++ b/src/app/pages/transaction/evm-transaction/evm-message/evm-message.component.ts @@ -24,6 +24,7 @@ export class EvmMessageComponent { data: string; }[]; data: string; + dataDecoded?: string; }[]; inputDataType = { @@ -41,8 +42,9 @@ export class EvmMessageComponent { isContractVerified = false; isCreateContract = false; arrTopicDecode = []; - interfaceCoder: Interface; contractAddressAbi = ''; + topicsDecoded = []; + abiContractData = []; constructor( private transactionService: TransactionService, @@ -58,51 +60,45 @@ export class EvmMessageComponent { this.typeInput = this.inputDataType.ORIGINAL; } this.getMethodName(this.inputDataRaw['methodId']); - this.getProxyContractAbi(this.transaction?.to); + this.getAbiList(); } changeType(data) { this.typeInput = data; } - getProxyContractAbi(address) { - this.contractAddressAbi = this.transaction?.to; - this.contractService.getProxyContractAbi(address).subscribe({ - next: (res) => { - this.contractAddressAbi = - _.get(res, 'evm_smart_contract[0].evm_proxy_histories[0].implementation_contract') || this.contractAddressAbi; - }, - complete: () => { - this.getDataDecoded(); - }, - }); - } - - getDataDecoded() { - if (!this.contractAddressAbi) { - return; - } + getAbiList() { + let listContract = this.transaction.eventLog.map((i) => i.address?.toLowerCase()); + listContract = _.uniq(listContract); - this.transactionService.getAbiContract(this.contractAddressAbi?.toLowerCase()).subscribe((res) => { - if (res?.evm_contract_verification?.length > 0 && res.evm_contract_verification[0]?.abi) { + this.transactionService.getListAbiContract(listContract).subscribe((res) => { + if (res?.evm_contract_verification?.length > 0) { this.isContractVerified = true; this.isDecoded = true; - this.interfaceCoder = new Interface(res.evm_contract_verification[0].abi); - - const value = parseEther('1.0'); - const rawData = this.interfaceCoder.parseTransaction({ data: '0x' + this.transaction?.inputData, value }); - if (rawData?.fragment?.inputs?.length > 0) { - this.getListTopicDecode(); - this.inputDataRaw['name'] = - this.interfaceCoder.getFunction(rawData?.fragment?.name)?.format() || rawData.name; - this.inputDataDecoded['name'] = rawData.name; - this.inputDataDecoded['params'] = rawData?.fragment?.inputs.map((item, index) => { - return { - name: item.name, - type: item.type, - value: rawData.args[index], - }; - }); + this.abiContractData = res?.evm_contract_verification.map((i) => ({ + contractAddress: i.contract_address, + abi: i.abi, + interfaceCoder: new Interface(i.abi), + })); + + const abiInfo = this.abiContractData.find((f) => f.contractAddress === this.transaction?.to); + if (abiInfo.abi) { + const value = parseEther('1.0'); + const rawData = abiInfo.interfaceCoder.parseTransaction({ data: '0x' + this.transaction?.inputData, value }); + if (rawData?.fragment?.inputs?.length > 0) { + this.getListTopicDecode(); + + this.inputDataRaw['name'] = + abiInfo.interfaceCoder.getFunction(rawData?.fragment?.name)?.format() || rawData.name; + this.inputDataDecoded['name'] = rawData.name; + this.inputDataDecoded['params'] = rawData?.fragment?.inputs.map((item, index) => { + return { + name: item.name, + type: item.type, + value: rawData.args[index], + }; + }); + } } } }); @@ -110,18 +106,55 @@ export class EvmMessageComponent { getListTopicDecode() { this.transaction.eventLog.forEach((element, index) => { - let arrTopicTemp = element?.evm_signature_mapping_topic || []; try { - const arrTemp = - this.interfaceCoder - .decodeEventLog(element.topic0, `0x${this.transaction?.inputData}`, element.topics) - .toArray() || []; - arrTopicTemp = [...this.arrTopicDecode[index], ...arrTemp]; - } catch (e) {} - - this.arrTopicDecode[index] = arrTopicTemp; + let decoded = []; + + const abiInfo = this.abiContractData.find((f) => f.contractAddress === element.address); + + if (abiInfo.abi) { + element.data = element?.data?.replace('\\x', ''); + const paramsDecode = abiInfo.interfaceCoder.parseLog({ + topics: element.topics?.filter((f) => f), + data: `0x${element.data || this.transaction?.inputData}`, + }); + + const params = paramsDecode?.fragment?.inputs.map((i) => `${i.type} ${i.indexed ? 'indexed' : ''} ${i.name}`); + const decodeTopic0 = `> ${paramsDecode?.fragment?.name}(${params.join(', ')})`; + + decoded = [ + { + index: 0, + decode: decodeTopic0, + value: element.topics[0], + }, + ]; + + if (paramsDecode?.fragment?.inputs?.length > 0) { + const param = paramsDecode?.fragment?.inputs.map((item, idx) => { + return { + index: idx + 1, + name: item.name, + type: item.type, + isLink: item.type === 'address' ? true : false, + isAllowSwitchDecode: true, + value: element.topics[idx + 1], + decode: paramsDecode.args[idx]?.toString(), + indexed: item.indexed, + }; + }); + const dataDecoded = param + .filter((f) => !f.indexed) + .map((i) => i.decode) + .join(', '); + element.dataDecoded = dataDecoded; + decoded = [...decoded, ...param]; + } + } + this.topicsDecoded[index] = decoded; + } catch (e) { + console.log(e); + } }); - this.arrTopicDecode = [...this.arrTopicDecode] } getMethodName(methodId) { diff --git a/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.html b/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.html index 41c13d6eb..398a31ccf 100644 --- a/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.html +++ b/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.html @@ -22,31 +22,30 @@ Topics
- -
-
{{ idx }}
-
-
- {{ top }} -
-
-
-
-
+ +
- {{ 'Data' }} + Data
- -
{{ eventLog.data }}
+
diff --git a/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.scss b/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.scss index b38275bf5..d1479a2d8 100644 --- a/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.scss +++ b/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.scss @@ -46,14 +46,6 @@ max-width: calc(100% - 38px); } - .highlight { - width: max-content; - max-width: 98%; - padding: 4px 8px; - border-radius: 4px; - background-color: var(--aura-gray-10); - } - .event-log-msg { color: var(--aura-gray-3); } diff --git a/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.ts b/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.ts index bad6e0a7a..a6f7b1268 100644 --- a/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.ts +++ b/src/app/pages/transaction/evm-transaction/evm-transaction-event-log/evm-transaction-event-log.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; @Component({ selector: 'app-evm-transaction-event-log', @@ -6,8 +6,8 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core styleUrls: ['./evm-transaction-event-log.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class EvmTransactionEventLogComponent implements OnInit { - @Input() arrTopicDecode; +export class EvmTransactionEventLogComponent { + @Input() topicsDecoded; @Input() eventLog: { id: number; contractName?: string; @@ -17,13 +17,7 @@ export class EvmTransactionEventLogComponent implements OnInit { data: string; }[]; data: string; + dataDecoded?: string; }; @Input() index; - - ngOnInit(): void { - if (this.eventLog?.data) { - this.eventLog['data'] = this.eventLog?.data.replace('\\x', ''); - } - } } - diff --git a/src/app/pages/transaction/transaction.module.ts b/src/app/pages/transaction/transaction.module.ts index 2ace90e16..899e8e721 100644 --- a/src/app/pages/transaction/transaction.module.ts +++ b/src/app/pages/transaction/transaction.module.ts @@ -26,6 +26,7 @@ import { TransactionMessagesComponent } from './transaction-messages/transaction import { TransactionRoutingModule } from './transaction-routing.module'; import { TransactionComponent } from './transaction.component'; import { EvmInternalTransactionsComponent } from './evm-transaction/evm-internal-transactions/evm-internal-transactions.component'; +import { DecodeMessageComponent } from './evm-transaction/decode-message/decode-message.component'; @NgModule({ declarations: [ @@ -38,6 +39,7 @@ import { EvmInternalTransactionsComponent } from './evm-transaction/evm-internal EvmMessageComponent, EvmTransactionEventLogComponent, EvmInternalTransactionsComponent, + DecodeMessageComponent ], imports: [ CommonModule, diff --git a/src/assets/config/config.json b/src/assets/config/config.json index 2aeab7379..4b01a0926 100644 --- a/src/assets/config/config.json +++ b/src/assets/config/config.json @@ -147,4 +147,4 @@ } } } -} +} \ No newline at end of file