mirror of
https://github.com/mempool/mempool.git
synced 2025-09-21 17:11:45 +02:00
Merge pull request #5920 from mempool/natsoni/fix-the-filters
Crop large OP_RETURN data in tx details section
This commit is contained in:
@@ -327,7 +327,7 @@
|
||||
@if (vout.isRunestone) {
|
||||
<button (click)="toggleOrdData(tx.txid, 'vout', vindex)" type="button" class="btn btn-sm badge badge-ord">Runestone</button>
|
||||
} @else {
|
||||
<a placement="bottom" [ngbTooltip]="vout.scriptpubkey_asm | hex2ascii"><span *ngIf="vout.scriptpubkey_asm !== 'OP_RETURN'" class="badge badge-secondary scriptmessage">{{ vout.scriptpubkey_asm | hex2ascii }}</span></a>
|
||||
<a placement="bottom" [ngbTooltip]="vout.scriptpubkey_asm | slice:0:200 | hex2ascii"><span *ngIf="vout.scriptpubkey_asm !== 'OP_RETURN'" class="badge badge-secondary scriptmessage">{{ vout.scriptpubkey_asm | slice:0:200 | hex2ascii }}</span></a>
|
||||
}
|
||||
</ng-template>
|
||||
<ng-template #otherPubkeyType>{{ vout.scriptpubkey_type | scriptpubkeyType }}</ng-template>
|
||||
@@ -384,15 +384,50 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="transactions-list.scriptpubkey.asm|ScriptPubKey (ASM)">ScriptPubKey (ASM)</td>
|
||||
<td style="text-align: left;" [innerHTML]="vout.scriptpubkey_asm | asmStyler"></td>
|
||||
<td style="text-align: left;">
|
||||
<app-asm [asm]="vout.scriptpubkey_asm" [crop]="showFullScriptPubkeyAsm[vindex] ? 0 : 1000"></app-asm>
|
||||
<div *ngIf="vout.scriptpubkey_asm.length > 1000" style="display: flex;">
|
||||
<span *ngIf="!showFullScriptPubkeyAsm[vindex]">...</span>
|
||||
<label class="btn btn-sm btn-primary mt-2" (click)="toggleShowFullScriptPubkeyAsm(vindex)" style="margin-left: auto;">
|
||||
<span *ngIf="!showFullScriptPubkeyAsm[vindex]" i18n="show-all">Show all</span>
|
||||
<span *ngIf="showFullScriptPubkeyAsm[vindex]" i18n="show-less">Show less</span>
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transactions-list.scriptpubkey.hex|ScriptPubKey (HEX)">ScriptPubKey (HEX)</td>
|
||||
<td style="text-align: left;">{{ vout.scriptpubkey }}</td>
|
||||
<td style="text-align: left;">
|
||||
@if (!showFullScriptPubkeyHex[vindex]) {
|
||||
{{ vout.scriptpubkey | slice:0:1000 }}
|
||||
} @else {
|
||||
{{ vout.scriptpubkey }}
|
||||
}
|
||||
<div *ngIf="vout.scriptpubkey.length > 1000" style="display: flex;">
|
||||
<span *ngIf="!showFullScriptPubkeyHex[vindex]">...</span>
|
||||
<label class="btn btn-sm btn-primary mt-2" (click)="toggleShowFullScriptPubkeyHex(vindex)" style="margin-left: auto;">
|
||||
<span *ngIf="!showFullScriptPubkeyHex[vindex]" i18n="show-all">Show all</span>
|
||||
<span *ngIf="showFullScriptPubkeyHex[vindex]" i18n="show-less">Show less</span>
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="vout.scriptpubkey_type == 'op_return'">
|
||||
<td>OP_RETURN <span>data</span></td>
|
||||
<td style="text-align: left;">{{ vout.scriptpubkey_asm | hex2ascii }}</td>
|
||||
<td style="text-align: left; word-break: break-word;">
|
||||
@if (!showFullOpReturnData[vindex]) {
|
||||
{{ (vout.scriptpubkey_asm | hex2ascii | slice:0:1000) }}
|
||||
} @else {
|
||||
{{ vout.scriptpubkey_asm | hex2ascii }}
|
||||
}
|
||||
<div *ngIf="(vout.scriptpubkey_asm | hex2ascii).length > 1000" style="display: flex;">
|
||||
<span *ngIf="!showFullOpReturnData[vindex]">...</span>
|
||||
<label class="btn btn-sm btn-primary mt-2" (click)="toggleShowFullOpReturnData(vindex)" style="margin-left: auto;">
|
||||
<span *ngIf="!showFullOpReturnData[vindex]" i18n="show-all">Show all</span>
|
||||
<span *ngIf="showFullOpReturnData[vindex]" i18n="show-less">Show less</span>
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="vout.scriptpubkey_type">
|
||||
<td i18n="transactions-list.vout.scriptpubkey-type">Type</td>
|
||||
|
@@ -62,6 +62,9 @@ export class TransactionsListComponent implements OnInit, OnChanges, OnDestroy {
|
||||
outputRowLimit: number = 12;
|
||||
showFullScript: { [vinIndex: number]: boolean } = {};
|
||||
showFullWitness: { [vinIndex: number]: { [witnessIndex: number]: boolean } } = {};
|
||||
showFullScriptPubkeyAsm: { [voutIndex: number]: boolean } = {};
|
||||
showFullScriptPubkeyHex: { [voutIndex: number]: boolean } = {};
|
||||
showFullOpReturnData: { [voutIndex: number]: boolean } = {};
|
||||
showOrdData: { [key: string]: { show: boolean; inscriptions?: Inscription[]; runestone?: Runestone, runeInfo?: { [id: string]: { etching: Etching; txid: string; } }; } } = {};
|
||||
similarityMatches: Map<string, Map<string, { score: number, match: AddressMatch, group: number }>> = new Map();
|
||||
|
||||
@@ -516,6 +519,18 @@ export class TransactionsListComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.showFullWitness[vinIndex][witnessIndex] = !this.showFullWitness[vinIndex][witnessIndex];
|
||||
}
|
||||
|
||||
toggleShowFullScriptPubkeyAsm(voutIndex: number): void {
|
||||
this.showFullScriptPubkeyAsm[voutIndex] = !this.showFullScriptPubkeyAsm[voutIndex];
|
||||
}
|
||||
|
||||
toggleShowFullScriptPubkeyHex(voutIndex: number): void {
|
||||
this.showFullScriptPubkeyHex[voutIndex] = !this.showFullScriptPubkeyHex[voutIndex];
|
||||
}
|
||||
|
||||
toggleShowFullOpReturnData(voutIndex: number): void {
|
||||
this.showFullOpReturnData[voutIndex] = !this.showFullOpReturnData[voutIndex];
|
||||
}
|
||||
|
||||
toggleOrdData(txid: string, type: 'vin' | 'vout', index: number) {
|
||||
const tx = this.transactions.find((tx) => tx.txid === txid);
|
||||
if (!tx) {
|
||||
|
@@ -28,7 +28,7 @@ export class AsmComponent {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['asm']) {
|
||||
if (changes['asm'] || changes['crop']) {
|
||||
this.parseASM();
|
||||
}
|
||||
}
|
||||
@@ -36,14 +36,35 @@ export class AsmComponent {
|
||||
parseASM(): void {
|
||||
let instructions = this.asm.split('OP_');
|
||||
// trim instructions to a whole number of instructions with at most `crop` characters total
|
||||
if (this.crop) {
|
||||
if (this.crop && this.asm.length > this.crop) {
|
||||
let chars = 0;
|
||||
for (let i = 0; i < instructions.length; i++) {
|
||||
chars += instructions[i].length + 3;
|
||||
if (chars > this.crop) {
|
||||
if (chars + instructions[i].length + 3 > this.crop) {
|
||||
let croppedInstruction = instructions[i];
|
||||
instructions = instructions.slice(0, i);
|
||||
// add cropped instruction
|
||||
let remainingChars = this.crop - chars;
|
||||
let parts = croppedInstruction.split(' ');
|
||||
// only render this instruction if there is space for the instruction name and a few args
|
||||
if (remainingChars > parts[0].length + 10) {
|
||||
remainingChars -= parts[0].length + 1;
|
||||
for (let j = 1; j < parts.length; j++) {
|
||||
const arg = parts[j];
|
||||
if (remainingChars >= arg.length) {
|
||||
remainingChars -= arg.length + 1;
|
||||
} else {
|
||||
// crop this argument
|
||||
parts[j] = arg.slice(0, remainingChars);
|
||||
// and remove all following arguments
|
||||
parts = parts.slice(0, j + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
instructions.push(`${parts.join(' ')}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
chars += instructions[i].length + 3;
|
||||
}
|
||||
}
|
||||
this.instructions = instructions.filter(instruction => instruction.trim() !== '').map(instruction => {
|
||||
@@ -82,6 +103,7 @@ export class AsmComponent {
|
||||
['ELSE', 'control'],
|
||||
['ENDIF', 'control'],
|
||||
['VERIFY', 'control'],
|
||||
['RETURN', 'control'],
|
||||
...Array.from({length: 70}, (_, i) => [`RETURN_${i + 186}`, 'control']),
|
||||
|
||||
// Stack
|
||||
|
Reference in New Issue
Block a user