Skip to content

Commit

Permalink
[Feat][655] Enhance event log (#3571)
Browse files Browse the repository at this point in the history
* 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 <tam2391@gmail.com>
  • Loading branch information
TranTrungTien and tamvb2391 committed Jul 1, 2024
1 parent 5f61503 commit 7e2e8e4
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 101 deletions.
28 changes: 28 additions & 0 deletions src/app/core/services/transaction.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>(this.graphUrl, {
query: operationsDoc,
variables: {
address: addressList,
},
operationName: 'getListAbiContract',
})
.pipe(map((res) => (res?.data ? res?.data[this.envDB] : null)));
}

getListMappingName(methodId: any): Observable<any> {
const operationsDoc = `
query queryListNameMethod($limit: Int = 100, $methodId: [String!] = null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<div class="decode-message d-flex justify-content-start align-items-start col-12 d-flex gap-2" [class]="!isMobile ? 'flex-nowrap' : 'flex-wrap'">
<div class="d-flex gap-2 justify-content-center align-items-start flex-nowrap">
<p class="name">
{{index}} <span *ngIf="name">: {{name}}</span>
</p>
<div *ngIf="isAllowSwitchDecode" class="dropdown aura-dropdown mr-4 btn-item">
<button style="height: 26px; min-width: max-content;"
class="button button--sm button--md-mob button-pill button-outline button-dropdown w-100" type="button"
data-bs-toggle="dropdown" aria-expanded="false">
<span>{{type}}</span>
</button>
<ul class="dropdown-menu aura-dropdown-menu">
<li (click)="onHex()" class="dropdown-item cursor-pointer" [class.active]="type === 'Hex'">
<button type="button" class="button p-0">Hex</button>
</li>
<li (click)="onDecode()" class="dropdown-item cursor-pointer" [class.active]="type === 'Decode'">
<button type="button" class="button p-0">Decode</button>
</li>
</ul>
</div>
</div>
<div *ngIf="!isAllowSwitchDecode" class="data flex-grow-1 font-space-mono">
<p class="mb-1 font-space-mono">{{value}}</p>
<p class="mb-0 font-space-mono" [innerHTML]="decode | highlight_function"></p>
</div>
<div *ngIf="isAllowSwitchDecode" class="data flex-grow-1 font-space-mono" [class]="isHighlight ? 'highlight' : ''">
<a *ngIf="isLink && type === 'Decode'" class="text--primary" routerLink="/address/{{ data }}">
{{data}}
</a>
<span *ngIf="!isLink || type === 'Hex'">{{data}}</span>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,18 @@
<div class="message-container text--white">
<div class="row mb-4 mb-lg-2 body justify-content-between justify-content-lg-start">
<div class="col-12 col-lg-2 mb-2 mb-lg-0 text--gray-4 body">
{{ 'Method' }}
</div>
<div class="col-12 col-lg-10 text--gray-1">{{ method }}</div>
</div>

<div
class="row mb-3 body justify-content-between justify-content-lg-start align-items-center"
*ngIf="transaction?.contractAddress > 0">
<div class="col-auto col-lg-2 text--gray-4">Contract Address</div>
<div class="col-auto col-lg-10">
<div class="col-auto">
<app-name-tag
[value]="transaction?.contractAddress | beautyAddress"
[fullText]="true"
[linkRouter]="['/evm-contracts', transaction?.contractAddress?.toLowerCase()]"
[iconContract]="true"></app-name-tag>
</div>
</div>

<div class="d-flex flex-wrap mb-4" *ngIf="transaction?.inputData">
<div class="col-12 col-lg-2 mb-2 mb-lg-0 text--gray-4 body">
{{ 'Input Data' }}
</div>
<div class="col-12 col-lg-10">
<div class="col-12">
<div class="button-switch-stage box-input-data mt-lg-0 mb-2">
<button
class="button body-04 {{ isCreateContract ? 'text--gray-7 pe-none' : '' }}"
Expand Down Expand Up @@ -139,10 +128,10 @@
<div class="mb-6">Transaction Receipt Event Logs ({{ eventLog?.length }})</div>
<div class="">
<app-evm-transaction-event-log
*ngFor="let log of eventLog; index as idx"
[eventLog]="log"
[arrTopicDecode]="arrTopicDecode"
[index]="idx">
*ngFor="let topic of topicsDecoded; let i = index"
[eventLog]="eventLog[i]"
[topicsDecoded]="topic"
[index]="i">
</app-evm-transaction-event-log>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
border-radius: 8px;
padding: 12px;
max-height: 558px;
overflow: auto;
@media (min-width: 992px) {
max-height: 330px;
padding: 12px 16px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class EvmMessageComponent {
data: string;
}[];
data: string;
dataDecoded?: string;
}[];

inputDataType = {
Expand All @@ -41,8 +42,9 @@ export class EvmMessageComponent {
isContractVerified = false;
isCreateContract = false;
arrTopicDecode = [];
interfaceCoder: Interface;
contractAddressAbi = '';
topicsDecoded = [];
abiContractData = [];

constructor(
private transactionService: TransactionService,
Expand All @@ -58,70 +60,101 @@ 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],
};
});
}
}
}
});
}

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) {
Expand Down
Loading

0 comments on commit 7e2e8e4

Please sign in to comment.