Skip to content

Commit e6eb719

Browse files
committed
cat21-mint: live stats + latest cats; genesis $getBlock fix; misc
- cat21-mint: dynamic stats block (totalCats / proofOfCatWork) and a 2-row grid of the 6 latest cats linking to cat21.space, fed by Cat21ApiService.getStatus / getLatestCatNumbers. Intro copy uses the whitepaper's reveal language. - backend $getBlock: short-circuit genesis hashes (mainnet/test3/test4/ signet/regtest) to bypass \$indexBlock. Bitcoin Core's getrawtransaction hardcoded-rejects the genesis coinbase, so the standard index path ends as 404. Return the bare block via bitcoinCoreApi. - block.component: no previous-block arrow on block 1. Our electrs runs lightmode and can't serve the genesis tx; without this, navigating back from block 1 dead-ends in a 404. - block-ordpool-stats: status-detail wraps at 70ch (display: block, max-width: 70ch) so the cell content stays inside the layout.
1 parent 8eac7e6 commit e6eb719

7 files changed

Lines changed: 118 additions & 13 deletions

File tree

backend/src/api/blocks.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ import { DigitalArtifactAnalyserService, getFirstInscriptionHeight } from 'ordpo
2222
// HACK: force a given block for debugging reasons
2323
// const debugBlock = 839999;
2424
const debugBlock = null;
25+
26+
// HACK -- Ordpool: genesis hashes for the supported Bitcoin networks. Used
27+
// to short-circuit $getBlock for genesis (Core can't getrawtransaction the
28+
// genesis coinbase, so the standard index path 404s).
29+
const BLOCK_GENESIS_HASHES = [
30+
'000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', // mainnet
31+
'000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943', // testnet3
32+
'00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043', // testnet4
33+
'00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6', // signet
34+
'0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206', // regtest
35+
];
2536
import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository';
2637
import BlocksAuditsRepository from '../repositories/BlocksAuditsRepository';
2738
import cpfpRepository from '../repositories/CpfpRepository';
@@ -1518,6 +1529,14 @@ class Blocks {
15181529
return await bitcoinCoreApi.$getBlock(hash);
15191530
}
15201531

1532+
// HACK -- Ordpool: short-circuit genesis. Bitcoin Core treats the genesis
1533+
// coinbase tx as non-standard (getrawtransaction fails), so $indexBlock's
1534+
// call to $getTransactionsExtended throws and the route ends up as 404.
1535+
// Return the bare block from bitcoinCoreApi without trying to extend txs.
1536+
if (BLOCK_GENESIS_HASHES.includes(hash)) {
1537+
return await bitcoinCoreApi.$getBlock(hash);
1538+
}
1539+
15211540
// Bitcoin network, add our custom data on top
15221541
return await this.$indexBlock(hash);
15231542
}

frontend/src/app/components/_ordpool/cat21-mint/cat21-mint.component.html

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,33 @@ <h2 class="mt-2 mb-4">Mint a CAT-21 Ordinal</h2>
88

99
<p class="mx-auto" style="max-width: 800px;">
1010

11-
Did you know? Every Bitcoin transaction can mint a hidden cat.
12-
Rescue the cats and be part of the movement!
11+
Did you know? Every Bitcoin transaction can reveal a hidden cat.
12+
Mint the cats and be part of the movement!
13+
CAT-21 is a meme protocol from the creator of Ordpool.
14+
Minting is free; you pay only the network fee.
15+
Cats bring joy. Reveal yours.
1316

14-
<!-- <a href="https://cat21.space/" target="_blank" style="color:#FF9900; text-decoration: underline;">CAT-21</a> -->
15-
CAT-21 is a new meme protocol from the creator of Ordpool.
16-
The minting process is completely free.
17-
So take your chance while fees are low and rescue as many cats as you can!
17+
</p>
1818

19-
And while you're here, you might also want to <a href="https://github.com/ordpool-space/cat-21" target="_blank" style="color:#FF9900; text-decoration: underline;">read the whitepaper</a>.
19+
<ng-container *ngIf="latestCatNumbers$ | async as latest">
20+
<div *ngIf="latest.length"
21+
class="cat-grid mx-auto"
22+
style="max-width: 800px; margin-top: 16px;">
23+
<a *ngFor="let n of latest"
24+
[href]="'https://cat21.space/cat/' + n"
25+
target="_blank"
26+
rel="noopener"
27+
[title]="'Cat #' + n">
28+
<img [src]="catImageUrl(n)"
29+
[alt]="'Cat #' + n"
30+
loading="lazy">
31+
</a>
32+
</div>
33+
</ng-container>
2034

35+
<p class="text-center text-muted" style="margin-top: 16px;">
36+
Wanna see more? Every revealed cat lives at
37+
<a href="https://cat21.space/" target="_blank" style="color:#FF9900; text-decoration: underline;">cat21.space</a>.
2138
</p>
2239

2340
</div>
@@ -232,6 +249,13 @@ <h2 class="mt-2 mb-4">Mint a CAT-21 Ordinal</h2>
232249

233250
</ng-container>
234251
</div>
252+
253+
<p class="text-center mt-4 mx-auto" style="max-width: 800px;" *ngIf="mintStatus$ | async as s">
254+
So far, <strong>{{ s.totalCats | number }} cats</strong> have been revealed.<br>
255+
<strong>Proof of Cat Work:</strong>
256+
{{ s.proofOfCatWork | number }} sats
257+
(≈ {{ s.proofOfCatWork / 1e8 | number:'1.2-2' }} BTC).
258+
</p>
235259
</div>
236260

237261
<div class="container-xl pl-5 pr-5" *ngIf="!enableCat21Mint">

frontend/src/app/components/_ordpool/cat21-mint/cat21-mint.component.scss

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,28 @@
99
background-color: #276134;
1010

1111
}
12+
13+
// Always exactly 2 rows of cat thumbnails.
14+
// Column count adapts to breakpoint; we fetch 12 cats and clip the rest with
15+
// overflow:hidden so the row count stays at 2 regardless of viewport width.
16+
.cat-grid {
17+
display: grid;
18+
gap: 8px;
19+
justify-content: center;
20+
overflow: hidden;
21+
max-height: 200px; // 96*2 + 8 gap → exactly 2 rows at 96px
22+
23+
// Cells are always 96px; the column count adapts to keep 2 rows.
24+
grid-template-columns: repeat(3, 96px); // xs: 3 × 2 = 6 cats
25+
@media (min-width: 576px) { grid-template-columns: repeat(4, 96px); } // sm: 4 × 2 = 8
26+
@media (min-width: 768px) { grid-template-columns: repeat(5, 96px); } // md: 5 × 2 = 10
27+
@media (min-width: 992px) { grid-template-columns: repeat(6, 96px); } // lg+: 6 × 2 = 12
28+
29+
> a > img {
30+
width: 96px;
31+
height: 96px;
32+
image-rendering: pixelated;
33+
border-radius: 4px;
34+
display: block;
35+
}
36+
}

frontend/src/app/components/_ordpool/cat21-mint/cat21-mint.component.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ export class Cat21MintComponent implements OnInit {
3737
connectedWallet$ = this.walletService.connectedWallet$;
3838
selectedFeeRate$ = new BehaviorSubject<number>(0);
3939

40+
mintStatus$ = this.cat21ApiService.getStatus().pipe(
41+
catchError(() => of(null))
42+
);
43+
latestCatNumbers$ = this.cat21ApiService.getLatestCatNumbers(12).pipe(
44+
map(r => r.catNumbers),
45+
catchError(() => of([] as number[]))
46+
);
47+
catImageUrl = (n: number) => this.cat21ApiService.getCatImageUrl(n);
48+
4049
selectedPaymentOutput: {
4150
simulation: SimulateTransactionResult;
4251
paymentOutput: TxnOutput;

frontend/src/app/components/_ordpool/ordpool-stats/block-ordpool-stats.component.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,6 @@ small {
5151
.status-detail {
5252
color: var(--transparent-fg);
5353
font-size: 0.9em;
54+
display: block;
55+
max-width: 70ch;
5456
}

frontend/src/app/components/block/block.component.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,11 @@ export class BlockComponent implements OnInit, OnDestroy {
564564

565565
setNextAndPreviousBlockLink(): void {
566566
if (this.latestBlock) {
567-
if (!this.blockHeight){
567+
// HACK -- Ordpool: also hide prev-arrow on block 1.
568+
// ordpool-backend can't index genesis (Bitcoin Core treats the genesis
569+
// coinbase tx specially, $getTransactionsExtended fails), so navigating
570+
// to block 0 returns 404. Don't offer that link.
571+
if (!this.blockHeight || this.blockHeight === 1){
568572
this.showPreviousBlocklink = false;
569573
} else {
570574
this.showPreviousBlocklink = true;

frontend/src/app/services/ordinals/cat21-api.service.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import { HttpClient } from '@angular/common/http';
22
import { Injectable, inject } from '@angular/core';
3-
import { Observable, interval, startWith, switchMap, takeWhile } from 'rxjs';
3+
import { Observable, interval, shareReplay, startWith, switchMap, takeWhile } from 'rxjs';
44

55
import { environment } from '../../../environments/environment';
66
import { WalletService } from './wallet.service';
77

88
export interface StatusResult {
9-
network: string;
10-
indexedCats: number;
11-
lastSuccessfulExecution: string;
12-
uptime: number;
9+
totalCats: number;
10+
lastSyncedCatNumber: number;
11+
proofOfCatWork: number;
12+
}
13+
14+
export interface CatNumbersResult {
15+
catNumbers: number[];
16+
total: number;
17+
currentPage: number;
18+
itemsPerPage: number;
1319
}
1420

1521
export interface Cat21 {
@@ -112,6 +118,22 @@ export class Cat21ApiService {
112118
return this.http.get<WhitelistStatusResult>(`${this.baseUrl}/whitelist/status/${walletAddress}`);
113119
}
114120

121+
getStatus(): Observable<StatusResult> {
122+
return this.http.get<StatusResult>(`${this.baseUrl}/api/status`).pipe(
123+
shareReplay({ bufferSize: 1, refCount: true })
124+
);
125+
}
126+
127+
getLatestCatNumbers(itemsPerPage: number): Observable<CatNumbersResult> {
128+
return this.http.get<CatNumbersResult>(`${this.baseUrl}/api/cats/numbers/${itemsPerPage}/1`).pipe(
129+
shareReplay({ bufferSize: 1, refCount: true })
130+
);
131+
}
132+
133+
getCatImageUrl(catNumber: number): string {
134+
return `${this.baseUrl}/api/cat/${catNumber}/image.webp`;
135+
}
136+
115137
/*
116138
getWhitelistStatusPolled(walletAddress: string): Observable<WhitelistStatusResult> {
117139
return interval(10000).pipe(

0 commit comments

Comments
 (0)