Merge pull request #3380 from mempool/mononaut/mempool-effective-rates

Use effective fee rates in mempool block visualizations & tooltips
This commit is contained in:
softsimon 2023-05-04 01:19:40 +04:00 committed by GitHub
commit 90154aec83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 37 additions and 8 deletions

View File

@ -83,6 +83,7 @@ export class Common {
fee: tx.fee, fee: tx.fee,
vsize: tx.weight / 4, vsize: tx.weight / 4,
value: tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0), value: tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0),
rate: tx.effectiveFeePerVsize,
}; };
} }

View File

@ -138,6 +138,7 @@ class MempoolBlocks {
for (let i = 0; i < Math.max(mempoolBlocks.length, prevBlocks.length); i++) { for (let i = 0; i < Math.max(mempoolBlocks.length, prevBlocks.length); i++) {
let added: TransactionStripped[] = []; let added: TransactionStripped[] = [];
let removed: string[] = []; let removed: string[] = [];
const changed: { txid: string, rate: number | undefined }[] = [];
if (mempoolBlocks[i] && !prevBlocks[i]) { if (mempoolBlocks[i] && !prevBlocks[i]) {
added = mempoolBlocks[i].transactions; added = mempoolBlocks[i].transactions;
} else if (!mempoolBlocks[i] && prevBlocks[i]) { } else if (!mempoolBlocks[i] && prevBlocks[i]) {
@ -146,7 +147,7 @@ class MempoolBlocks {
const prevIds = {}; const prevIds = {};
const newIds = {}; const newIds = {};
prevBlocks[i].transactions.forEach(tx => { prevBlocks[i].transactions.forEach(tx => {
prevIds[tx.txid] = true; prevIds[tx.txid] = tx;
}); });
mempoolBlocks[i].transactions.forEach(tx => { mempoolBlocks[i].transactions.forEach(tx => {
newIds[tx.txid] = true; newIds[tx.txid] = true;
@ -159,12 +160,15 @@ class MempoolBlocks {
mempoolBlocks[i].transactions.forEach(tx => { mempoolBlocks[i].transactions.forEach(tx => {
if (!prevIds[tx.txid]) { if (!prevIds[tx.txid]) {
added.push(tx); added.push(tx);
} else if (tx.rate !== prevIds[tx.txid].rate) {
changed.push({ txid: tx.txid, rate: tx.rate });
} }
}); });
} }
mempoolBlockDeltas.push({ mempoolBlockDeltas.push({
added, added,
removed removed,
changed,
}); });
} }
return mempoolBlockDeltas; return mempoolBlockDeltas;

View File

@ -58,6 +58,7 @@ export interface MempoolBlockWithTransactions extends MempoolBlock {
export interface MempoolBlockDelta { export interface MempoolBlockDelta {
added: TransactionStripped[]; added: TransactionStripped[];
removed: string[]; removed: string[];
changed: { txid: string, rate: number | undefined }[];
} }
interface VinStrippedToScriptsig { interface VinStrippedToScriptsig {
@ -149,6 +150,7 @@ export interface TransactionStripped {
fee: number; fee: number;
vsize: number; vsize: number;
value: number; value: number;
rate?: number; // effective fee rate
} }
export interface BlockExtension { export interface BlockExtension {

View File

@ -132,9 +132,9 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
} }
} }
update(add: TransactionStripped[], remove: string[], direction: string = 'left', resetLayout: boolean = false): void { update(add: TransactionStripped[], remove: string[], change: { txid: string, rate: number | undefined }[], direction: string = 'left', resetLayout: boolean = false): void {
if (this.scene) { if (this.scene) {
this.scene.update(add, remove, direction, resetLayout); this.scene.update(add, remove, change, direction, resetLayout);
this.start(); this.start();
} }
} }

View File

@ -150,7 +150,7 @@ export default class BlockScene {
this.updateAll(startTime, 200, direction); this.updateAll(startTime, 200, direction);
} }
update(add: TransactionStripped[], remove: string[], direction: string = 'left', resetLayout: boolean = false): void { update(add: TransactionStripped[], remove: string[], change: { txid: string, rate: number | undefined }[], direction: string = 'left', resetLayout: boolean = false): void {
const startTime = performance.now(); const startTime = performance.now();
const removed = this.removeBatch(remove, startTime, direction); const removed = this.removeBatch(remove, startTime, direction);
@ -172,6 +172,15 @@ export default class BlockScene {
this.place(tx); this.place(tx);
}); });
} else { } else {
// update effective rates
change.forEach(tx => {
if (this.txs[tx.txid]) {
this.txs[tx.txid].feerate = tx.rate || (this.txs[tx.txid].fee / this.txs[tx.txid].vsize);
this.txs[tx.txid].rate = tx.rate;
this.txs[tx.txid].dirty = true;
}
});
// try to insert new txs directly // try to insert new txs directly
const remaining = []; const remaining = [];
add.map(tx => new TxView(tx, this)).sort(feeRateDescending).forEach(tx => { add.map(tx => new TxView(tx, this)).sort(feeRateDescending).forEach(tx => {

View File

@ -36,6 +36,7 @@ export default class TxView implements TransactionStripped {
vsize: number; vsize: number;
value: number; value: number;
feerate: number; feerate: number;
rate?: number;
status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected'; status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected';
context?: 'projected' | 'actual'; context?: 'projected' | 'actual';
scene?: BlockScene; scene?: BlockScene;
@ -58,7 +59,8 @@ export default class TxView implements TransactionStripped {
this.fee = tx.fee; this.fee = tx.fee;
this.vsize = tx.vsize; this.vsize = tx.vsize;
this.value = tx.value; this.value = tx.value;
this.feerate = tx.fee / tx.vsize; this.feerate = tx.rate || (tx.fee / tx.vsize); // sort by effective fee rate where available
this.rate = tx.rate;
this.status = tx.status; this.status = tx.status;
this.initialised = false; this.initialised = false;
this.vertexArray = scene.vertexArray; this.vertexArray = scene.vertexArray;
@ -157,7 +159,8 @@ export default class TxView implements TransactionStripped {
} }
getColor(): Color { getColor(): Color {
const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, this.feerate) < feeLvl) - 1; const rate = this.fee / this.vsize; // color by simple single-tx fee rate
const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1;
const feeLevelColor = feeColors[feeLevelIndex] || feeColors[mempoolFeeColors.length - 1]; const feeLevelColor = feeColors[feeLevelIndex] || feeColors[mempoolFeeColors.length - 1];
// Normal mode // Normal mode
if (!this.scene?.highlightingEnabled) { if (!this.scene?.highlightingEnabled) {

View File

@ -28,6 +28,12 @@
{{ feeRate | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span> {{ feeRate | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
</td> </td>
</tr> </tr>
<tr *ngIf="effectiveRate && effectiveRate !== feeRate">
<td class="td-width" i18n="transaction.effective-fee-rate|Effective transaction fee rate">Effective fee rate</td>
<td>
{{ effectiveRate | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
</td>
</tr>
<tr> <tr>
<td class="td-width" i18n="transaction.vsize|Transaction Virtual Size">Virtual size</td> <td class="td-width" i18n="transaction.vsize|Transaction Virtual Size">Virtual size</td>
<td [innerHTML]="'&lrm;' + (vsize | vbytes: 2)"></td> <td [innerHTML]="'&lrm;' + (vsize | vbytes: 2)"></td>

View File

@ -20,6 +20,7 @@ export class BlockOverviewTooltipComponent implements OnChanges {
value = 0; value = 0;
vsize = 1; vsize = 1;
feeRate = 0; feeRate = 0;
effectiveRate;
tooltipPosition: Position = { x: 0, y: 0 }; tooltipPosition: Position = { x: 0, y: 0 };
@ -51,6 +52,7 @@ export class BlockOverviewTooltipComponent implements OnChanges {
this.value = tx.value || 0; this.value = tx.value || 0;
this.vsize = tx.vsize || 1; this.vsize = tx.vsize || 1;
this.feeRate = this.fee / this.vsize; this.feeRate = this.fee / this.vsize;
this.effectiveRate = tx.rate;
} }
} }
} }

View File

@ -99,7 +99,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
const direction = (this.blockIndex == null || this.index < this.blockIndex) ? this.poolDirection : this.chainDirection; const direction = (this.blockIndex == null || this.index < this.blockIndex) ? this.poolDirection : this.chainDirection;
this.blockGraph.replace(delta.added, direction); this.blockGraph.replace(delta.added, direction);
} else { } else {
this.blockGraph.update(delta.added, delta.removed, blockMined ? this.chainDirection : this.poolDirection, blockMined); this.blockGraph.update(delta.added, delta.removed, delta.changed || [], blockMined ? this.chainDirection : this.poolDirection, blockMined);
} }
this.lastBlockHeight = this.stateService.latestBlockHeight; this.lastBlockHeight = this.stateService.latestBlockHeight;

View File

@ -57,6 +57,7 @@ export interface MempoolBlockWithTransactions extends MempoolBlock {
export interface MempoolBlockDelta { export interface MempoolBlockDelta {
added: TransactionStripped[], added: TransactionStripped[],
removed: string[], removed: string[],
changed?: { txid: string, rate: number | undefined }[];
} }
export interface MempoolInfo { export interface MempoolInfo {
@ -74,6 +75,7 @@ export interface TransactionStripped {
fee: number; fee: number;
vsize: number; vsize: number;
value: number; value: number;
rate?: number; // effective fee rate
status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected'; status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected';
context?: 'projected' | 'actual'; context?: 'projected' | 'actual';
} }