Skip to content

Commit

Permalink
Merge pull request #35 from flashbots/api-with-profits
Browse files Browse the repository at this point in the history
daily api: show profits
  • Loading branch information
metachris authored May 29, 2024
2 parents 4786b30 + e19bb7b commit 645b0cc
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 10 deletions.
13 changes: 9 additions & 4 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,21 @@ func (s *DatabaseService) GetBuilderProfits(since, until time.Time) (res []*Buil
return res, err
}

func (s *DatabaseService) GetStatsForTimerange(since, until time.Time, relay string) (relays []*TopRelayEntry, builders []*TopBuilderEntry, err error) {
func (s *DatabaseService) GetStatsForTimerange(since, until time.Time, relay string) (relays []*TopRelayEntry, builders []*TopBuilderEntry, builderProfits []*BuilderProfitEntry, err error) {
relays, err = s.GetTopRelays(since, until)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
builders, err = s.GetTopBuilders(since, until, relay)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
return relays, builders, nil
builderProfits, err = s.GetBuilderProfits(since, until)
if err != nil {
return nil, nil, nil, err
}

return relays, builders, builderProfits, nil
}

func (s *DatabaseService) GetDeliveredPayloadsForSlot(slot uint64) (res []*DataAPIPayloadDeliveredEntry, err error) {
Expand Down
1 change: 1 addition & 0 deletions services/website/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type HTMLDataDailyStats struct {

TopRelays []*database.TopRelayEntry
TopBuildersBySummary []*database.TopBuilderEntry
BuilderProfits []*database.BuilderProfitEntry
}

var funcMap = template.FuncMap{
Expand Down
139 changes: 139 additions & 0 deletions services/website/templates/daily-stats.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,147 @@ <h1 style="margin-bottom:0.3em;">MEV-Boost Stats for {{ .Day }}</h1>
</div>
</div>
</div>

<div class="pure-u-1 pure-u-md-1 stats-table" id="content-profitability">
<table id="table-builderprofit" class="table-builderprofit sortable pure-table pure-table-horizontal">
<thead>
<tr>
<th>Builder extra_data</th>
<th>Blocks</th>
<th>Blocks with profit</th>
<th>Blocks with subsidy</th>
<th id="th-builderprofit-profittotal">Overall profit (ETH)</th>
<th>Subsidies (ETH)</th>
</tr>
</thead>
<tbody>
{{ range .BuilderProfits }}
<tr>
<td class="builder-extradata">
{{ if .ExtraData }}<span style="white-space: pre;">{{ .ExtraData }}</span>{{ else }}&nbsp;{{ end }}
{{ if ne (len .Aliases) 0 }}
<span class="tooltip-wrapper">
<i class="tooltip-icon bi bi-info-circle" aria-describedby="tooltip-builderprofit-alias"></i>
<div class="tooltip builder-aliases" role="tooltip">
<b>extra_data values:</b>
<ul>
{{ range .Aliases }}
<li>{{ . }}</li>
{{ end }}
</ul>
<div class="arrow" data-popper-arrow></div>
</div>
</span>
{{ end }}
</td>
<td class="td-num-blocks" data-sort="{{ .NumBlocks }}">{{ .NumBlocks | prettyInt }}</td>
<td class="td-num-blocks-profit" data-sort="{{ .NumBlocksProfit }}">{{ .NumBlocksProfit | prettyInt }}</td>
<td class="td-num-blocks-subs" data-sort="{{ .NumBlocksSubsidised }}">{{ .NumBlocksSubsidised | prettyInt }}</td>
<td>{{ .ProfitTotal }}</td>
<td>{{ .SubsidiesTotal }}</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>

</div>


<script src="https://unpkg.com/@popperjs/core@2"></script>
<script>
const showEvents = ['mouseenter', 'focus'];
const hideEvents = ['mouseleave', 'blur'];

// Tooltip setup
for (const tooltip of document.getElementsByClassName("tooltip-wrapper")) {
const elTrigger = tooltip.querySelector(".tooltip-icon");
const elTooltip = tooltip.querySelector(".tooltip");
const popperInstance = Popper.createPopper(elTrigger, elTooltip, { modifiers: [{ name: 'offset', options: { offset: [0, 8] } }], });
function showTooltip() {
elTooltip.setAttribute('data-show', ''); // Show the tooltip
popperInstance.setOptions((options) => ({ ...options, modifiers: [...options.modifiers, { name: 'eventListeners', enabled: true },] }));
popperInstance.update(); // Update its position
}
function hideTooltip() {
elTooltip.removeAttribute('data-show'); // Hide the tooltip
popperInstance.setOptions((options) => ({ ...options, modifiers: [...options.modifiers, { name: 'eventListeners', enabled: false }] }));
}
showEvents.forEach((event) => {
elTrigger.addEventListener(event, showTooltip);
});

hideEvents.forEach((event) => {
elTrigger.addEventListener(event, hideTooltip);
});
}

showBuildersForRelay = function (relay) {
// hide all builders
for (const topBuildersTbody of document.getElementsByClassName("tbody-builders")) {
topBuildersTbody.style.display = "none";
}

// show specific builder
if (relay == "") {
document.getElementById("tbody-builders-all").style.display = "table-row-group";
} else {
document.getElementById("tbody-builders-" + relay).style.display = "table-row-group";
}
}

relayMouseOver = function (relay) {
showBuildersForRelay(relay)
}

relayMouseOut = function (relay) {
showBuildersForRelay("")
}

// COPY TABLES AS MARKDOWN TO CLIPBOARD
copyBuilders = function (e) {
e.stopPropagation();
var md = document.getElementById("md-builders").innerHTML;
navigator.clipboard.writeText(md);
document.getElementById("copy-builders-to-clipboard-icon").classList.remove("bi-clipboard");
document.getElementById("copy-builders-to-clipboard-icon").classList.add("bi-clipboard-check");
setTimeout(function () {
document.getElementById("copy-builders-to-clipboard-icon").classList.remove("bi-clipboard-check");
document.getElementById("copy-builders-to-clipboard-icon").classList.add("bi-clipboard");
}, 1000);
}

copyBuilderProfit = function (e) {
var md = document.getElementById("md-builderprofit").innerHTML.replace(/&amp;/g, '&');;
navigator.clipboard.writeText(md);
document.getElementById("copy-builderprofit-to-clipboard-icon").classList.remove("bi-clipboard");
document.getElementById("copy-builderprofit-to-clipboard-icon").classList.add("bi-clipboard-check");
setTimeout(function () {
document.getElementById("copy-builderprofit-to-clipboard-icon").classList.remove("bi-clipboard-check");
document.getElementById("copy-builderprofit-to-clipboard-icon").classList.add("bi-clipboard");
}, 1000);
}

copyRelays = function (e) {
e.stopPropagation();
var md = document.getElementById("md-relays").innerHTML;
navigator.clipboard.writeText(md);
document.getElementById("copy-relays-to-clipboard-icon").classList.remove("bi-clipboard");
document.getElementById("copy-relays-to-clipboard-icon").classList.add("bi-clipboard-check");
setTimeout(function () {
document.getElementById("copy-relays-to-clipboard-icon").classList.remove("bi-clipboard-check");
document.getElementById("copy-relays-to-clipboard-icon").classList.add("bi-clipboard");
}, 1000);
}

window.onload = (event) => {
// fix for table sorting (it's already sorted, and this click makes the sortable plugin think it has done it)
document.getElementById("th-builderprofit-profittotal").click()
}
</script>

<script src="/static/sortable.min.js"></script>


{{ end }}
15 changes: 9 additions & 6 deletions services/website/webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,17 +411,17 @@ func (srv *Webserver) handleBuilderProfitMarkdown(w http.ResponseWriter, req *ht
_, _ = w.Write(*srv.markdownBuilderProfit)
}

func (srv *Webserver) _getDailyStats(t time.Time) (since, until, minDate time.Time, relays []*database.TopRelayEntry, builders []*database.TopBuilderEntry, err error) {
func (srv *Webserver) _getDailyStats(t time.Time) (since, until, minDate time.Time, relays []*database.TopRelayEntry, builders []*database.TopBuilderEntry, builderProfits []*database.BuilderProfitEntry, err error) {
now := time.Now().UTC()
minDate = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC).Add(-24 * time.Hour).UTC()
if t.UTC().After(minDate.UTC()) {
return now, now, minDate, nil, nil, fmt.Errorf("date is too recent") //nolint:goerr113
return now, now, minDate, nil, nil, nil, fmt.Errorf("date is too recent") //nolint:goerr113
}

since = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.UTC)
until = time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, time.UTC)
relays, builders, err = srv.db.GetStatsForTimerange(since, until, "")
return since, until, minDate, relays, builders, err
relays, builders, builderProfits, err = srv.db.GetStatsForTimerange(since, until, "")
return since, until, minDate, relays, builders, builderProfits, err
}

func (srv *Webserver) handleDailyStats(w http.ResponseWriter, req *http.Request) {
Expand All @@ -434,11 +434,13 @@ func (srv *Webserver) handleDailyStats(w http.ResponseWriter, req *http.Request)
return
}

since, until, minDate, relays, builders, err := srv._getDailyStats(t)
srv.log.Infof("Loading daily stats for %s ...", t.Format("2006-01-02"))
since, until, minDate, relays, builders, builderProfits, err := srv._getDailyStats(t)
if err != nil {
srv.RespondError(w, http.StatusBadRequest, err.Error())
return
}
srv.log.Infof("Loading daily stats for %s completed. builderProfits: %d", t.Format("2006-01-02"), len(builderProfits))

dateNext := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.UTC).Add(24 * time.Hour).UTC()
dayNext := dateNext.Format("2006-01-02")
Expand All @@ -455,6 +457,7 @@ func (srv *Webserver) handleDailyStats(w http.ResponseWriter, req *http.Request)
TimeUntil: until.Format("2006-01-02 15:04"),
TopRelays: prepareRelaysEntries(relays),
TopBuildersBySummary: consolidateBuilderEntries(builders),
BuilderProfits: consolidateBuilderProfitEntries(builderProfits),
}

if srv.opts.Dev {
Expand Down Expand Up @@ -491,7 +494,7 @@ func (srv *Webserver) handleDailyStatsJSON(w http.ResponseWriter, req *http.Requ
return
}

_, _, _, relays, builders, err := srv._getDailyStats(t) //nolint:dogsled
_, _, _, relays, builders, _, err := srv._getDailyStats(t) //nolint:dogsled
if err != nil {
srv.RespondError(w, http.StatusBadRequest, err.Error())
return
Expand Down

0 comments on commit 645b0cc

Please sign in to comment.