From bfd574e77cbb9e4a562b26ef02d7f0b7dc594cb7 Mon Sep 17 00:00:00 2001 From: Hans Crypto Date: Sun, 7 Apr 2024 00:31:46 +0200 Subject: [PATCH 1/8] btc.SigHash.SINGLE_ANYONECANPAY --> btc.SigHash.ALL --- .../src/app/services/ordinals/cat21.service.helper.jest.ts | 2 +- frontend/src/app/services/ordinals/cat21.service.helper.ts | 6 +++--- frontend/src/app/services/ordinals/cat21.service.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/services/ordinals/cat21.service.helper.jest.ts b/frontend/src/app/services/ordinals/cat21.service.helper.jest.ts index 29ff7b052..172d70d36 100644 --- a/frontend/src/app/services/ordinals/cat21.service.helper.jest.ts +++ b/frontend/src/app/services/ordinals/cat21.service.helper.jest.ts @@ -354,7 +354,7 @@ createTransactionTestCases.forEach(({ info, walletType, recipientAddress, paymen afterEach(() => { if (result?.tx) { - result.tx.signIdx(dummyKeypair.dummyPrivateKey, 0, [btc.SigHash.SINGLE_ANYONECANPAY]); + result.tx.signIdx(dummyKeypair.dummyPrivateKey, 0, [btc.SigHash.ALL]); result.tx.finalize(); expect(result.tx.vsize).toBeGreaterThan(100); } diff --git a/frontend/src/app/services/ordinals/cat21.service.helper.ts b/frontend/src/app/services/ordinals/cat21.service.helper.ts index 4c7bf2a6b..9501ce670 100644 --- a/frontend/src/app/services/ordinals/cat21.service.helper.ts +++ b/frontend/src/app/services/ordinals/cat21.service.helper.ts @@ -364,7 +364,7 @@ export function createInput(walletType: KnownOrdinalWalletType, index: paymentOutput.vout, redeemScript, sequence: 0xfffffffd, // enables RBF - sighashType: btc.SigHash.SINGLE_ANYONECANPAY // 131 + sighashType: btc.SigHash.ALL }; if (isSegWit(paymentAddress)) { @@ -492,7 +492,7 @@ export async function signTransactionLeather(psbtBytes: Uint8Array, isMainnet: b const signRequestParams: LeatherSignPsbtRequestParams = { hex: psbtHex, - allowedSighash: [btc.SigHash.SINGLE_ANYONECANPAY], + allowedSighash: [btc.SigHash.ALL], signAtIndex: 0, network, broadcast: false // we will broadcast it via the Mempool API @@ -521,7 +521,7 @@ export function signTransactionAndBroadcastXverse(psbtBytes: Uint8Array, payment { address: paymentAddress, signingIndexes: [0], - sigHash: btc.SigHash.SINGLE_ANYONECANPAY // 131 + sigHash: btc.SigHash.ALL }, ], }, diff --git a/frontend/src/app/services/ordinals/cat21.service.ts b/frontend/src/app/services/ordinals/cat21.service.ts index 21a4285e1..5ba1a40b3 100644 --- a/frontend/src/app/services/ordinals/cat21.service.ts +++ b/frontend/src/app/services/ordinals/cat21.service.ts @@ -154,7 +154,7 @@ export class Cat21Service { this.isMainnet ); - result.tx.signIdx(dummyPrivateKey, 0, [btc.SigHash.SINGLE_ANYONECANPAY]); + result.tx.signIdx(dummyPrivateKey, 0, [btc.SigHash.ALL]); result.tx.finalize(); const vsize = result.tx.vsize; // 🎉 From c1b4c68d02c9e1e79b6bfdb0032565df4f70091c Mon Sep 17 00:00:00 2001 From: Hans Crypto Date: Sun, 7 Apr 2024 16:26:56 +0200 Subject: [PATCH 2/8] cleanup --- .../master-page/master-page.component.html | 4 +-- .../cat21-mint/cat21-mint.component.html | 34 ++++++++++++------- .../transaction/transaction.component.html | 4 +-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index 6afa516d5..26ae80d04 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -77,9 +77,9 @@

Mint a CAT-21 Ordinal

+
-
+
@@ -67,9 +68,9 @@

Mint a CAT-21 Ordinal

-
+
We are sorry, but we could not find find enough funds in your payment address! 😿
- No UTXO has enough value to execute a transaction at the set fees. + No UTXO has enough value to execute a transaction at the current fees. We recommend sending at least
    @@ -91,7 +92,7 @@

    Mint a CAT-21 Ordinal

    -
    +
    Show Expert Settings

    @@ -150,13 +151,8 @@

    Mint a CAT-21 Ordinal

    - {{ whitelistStatus$ | async | json }} - - - - - + - - - - - You can rescue some lovely cats at {{ whitelistStatus.mintingAllowedAt | date:'long' }}. - - - +

    + + You can rescue some lovely cats at {{ whitelistStatus.mintingAllowedAt | date:'long' }} ({{ whitelistStatus.level }}). +

    + - +
    @@ -237,6 +230,7 @@

    Mint a CAT-21 Ordinal

    +
diff --git a/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts b/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts index cd5c5f1f1..38026f300 100644 --- a/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts +++ b/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { hex } from '@scure/base'; -import { BehaviorSubject, catchError, combineLatest, map, of, shareReplay, switchMap, take, tap } from 'rxjs'; +import { BehaviorSubject, catchError, combineLatest, map, of, shareReplay, startWith, switchMap, take, tap } from 'rxjs'; import { environment } from '../../../../environments/environment'; import { Cat21ApiService } from '../../../services/ordinals/cat21-api.service'; @@ -41,6 +41,7 @@ export class Cat21MintComponent implements OnInit { simulation: SimulateTransactionResult; paymentOutput: TxnOutput; } | undefined = undefined; + utxosScanned = false; unisatShowWarningThreshold = 10 * 1000; @@ -62,6 +63,7 @@ export class Cat21MintComponent implements OnInit { tap(() => { this.utxoLoading = true; this.utxoError = ''; + this.cd.detectChanges(); }), switchMap(wallet => this.cat21Service.getUtxos(wallet?.paymentAddress).pipe( // retry({ count: 3, delay: 500 }), // Ordpool has a global interceptor for this, otherwise add this line @@ -78,6 +80,7 @@ export class Cat21MintComponent implements OnInit { tap(({ error }) => { this.utxoLoading = false; this.utxoError = error ? extractErrorMessage(error) : ''; + this.cd.detectChanges(); }) )) ); @@ -89,19 +92,23 @@ export class Cat21MintComponent implements OnInit { of({ mintingAllowed : true, mintingAllowedAt: new Date().toISOString(), + level: 'Public' }) : this.connectedWallet$.pipe( tap(() => { this.checkerLoading = true; this.checkerError = ''; + this.cd.detectChanges(); }), switchMap(wallet => this.cat21ApiService.getWhitelistStatus(wallet.ordinalsAddress).pipe( tap(() => { this.checkerLoading = false; this.checkerError = ''; + this.cd.detectChanges(); }), catchError(error => { this.checkerLoading = false; this.checkerError = error ? extractErrorMessage(error) : ''; + this.cd.detectChanges(); return of(undefined); }) ))); @@ -169,7 +176,10 @@ export class Cat21MintComponent implements OnInit { .slice(0, 10); // limit to max 10 elements }), // sets it to the largest available UTXO or to undefined - tap(simulateTransactions => this.selectedPaymentOutput = simulateTransactions[0]), + tap(simulateTransactions => { + this.selectedPaymentOutput = simulateTransactions[0]; + this.cd.detectChanges(); + }), shareReplay({ bufferSize: 1, refCount: true diff --git a/frontend/src/app/components/ordinals/cat21-mint/countdown-timer-component.ts b/frontend/src/app/components/ordinals/cat21-mint/countdown-timer-component.ts new file mode 100644 index 000000000..e77993689 --- /dev/null +++ b/frontend/src/app/components/ordinals/cat21-mint/countdown-timer-component.ts @@ -0,0 +1,42 @@ +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { Observable, interval, map, startWith, takeWhile } from 'rxjs'; + +@Component({ + selector: 'app-countdown-timer', + templateUrl: './countdown-timer.component.html', + styleUrls: ['./countdown-timer.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class CountdownTimerComponent implements OnInit { + + @Input() + targetDate: string | undefined; + countdown$: Observable; + + ngOnInit() { + this.countdown$ = interval(1000).pipe( + startWith(0), + map(() => { + + if (!this.targetDate) { + return ''; + } + + const now = new Date().getTime(); + const distance = new Date(this.targetDate).getTime() - now; + + if (distance < 0) { + return 'GO'; + } + + const days = Math.floor(distance / (1000 * 60 * 60 * 24)); + const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = Math.floor((distance % (1000 * 60)) / 1000); + + return `${days}d ${hours}h ${minutes}m ${seconds}s`; + }), + takeWhile(countdown => countdown !== 'GO', true) + ); + } +} diff --git a/frontend/src/app/components/ordinals/cat21-mint/countdown-timer.component.html b/frontend/src/app/components/ordinals/cat21-mint/countdown-timer.component.html new file mode 100644 index 000000000..a0bada7ac --- /dev/null +++ b/frontend/src/app/components/ordinals/cat21-mint/countdown-timer.component.html @@ -0,0 +1,13 @@ + +
+ +

CCC (Colossal Cat Countdown)

+

{{ countdown }}

+ + +
+
+ Don't sleep on the cats!
+ +
+
diff --git a/frontend/src/app/components/ordinals/cat21-mint/countdown-timer.component.scss b/frontend/src/app/components/ordinals/cat21-mint/countdown-timer.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 97fad170a..20454b83b 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -125,8 +125,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { digitalArtifactsPage = 1; getParsedDigitalArtifacts(): DigitalArtifact[] { - // just debugging: fake cat for every txn - // this.tx.locktime = 21; return DigitalArtifactsParserService.parse(this.tx); } diff --git a/frontend/src/app/services/ordinals/cat21.service.helper.ts b/frontend/src/app/services/ordinals/cat21.service.helper.ts index 9501ce670..abac9c9dc 100644 --- a/frontend/src/app/services/ordinals/cat21.service.helper.ts +++ b/frontend/src/app/services/ordinals/cat21.service.helper.ts @@ -432,7 +432,7 @@ export function createTransaction( const network: typeof btc.NETWORK = isMainnet ? btc.NETWORK : btc.TEST_NETWORK; - const lockTime = 21; // THIS is the most important part 😺 + const lockTime = 21; const tx = new btc.Transaction({ lockTime, allowLegacyWitnessUtxo: true, // for Unisat Legacy address diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 1b5e95e96..6bdecbbdc 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -119,6 +119,7 @@ import { FeesBoxClickableComponent } from '../components/fees-box-clickable/fees import { WalletConnectComponent } from '../components/ordinals/wallet-connect/wallet-connect.component'; import { HighlightModule } from 'ngx-highlightjs'; import { Cat21MintComponent } from '../components/ordinals/cat21-mint/cat21-mint.component'; +import { CountdownTimerComponent } from '../components/ordinals/cat21-mint/countdown-timer-component'; import { Cat21CollabComponent } from '../components/ordinals/cat21-collab/cat21-collab.component'; import { Cat21WhitelistCheckerComponent } from '../components/ordinals/cat21-whitelist-checker/cat21-whitelist-checker.component'; @@ -233,6 +234,7 @@ import { Cat21WhitelistCheckerComponent } from '../components/ordinals/cat21-whi FeesBoxClickableComponent, WalletConnectComponent, Cat21MintComponent, + CountdownTimerComponent, Cat21CollabComponent, Cat21WhitelistCheckerComponent ], From 86cc040c08431482c60e84b54c0968d6c66f75df Mon Sep 17 00:00:00 2001 From: Hans Crypto Date: Mon, 8 Apr 2024 01:35:21 +0200 Subject: [PATCH 7/8] polling to API (10 seconds to prevent too much load) --- .../ordinals/cat21-mint/cat21-mint.component.ts | 2 +- .../src/app/services/ordinals/cat21-api.service.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts b/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts index 38026f300..df49b18df 100644 --- a/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts +++ b/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts @@ -99,7 +99,7 @@ export class Cat21MintComponent implements OnInit { this.checkerError = ''; this.cd.detectChanges(); }), - switchMap(wallet => this.cat21ApiService.getWhitelistStatus(wallet.ordinalsAddress).pipe( + switchMap(wallet => this.cat21ApiService.getWhitelistStatusPolled(wallet.ordinalsAddress).pipe( tap(() => { this.checkerLoading = false; this.checkerError = ''; diff --git a/frontend/src/app/services/ordinals/cat21-api.service.ts b/frontend/src/app/services/ordinals/cat21-api.service.ts index 03ac49a85..9e3d32533 100644 --- a/frontend/src/app/services/ordinals/cat21-api.service.ts +++ b/frontend/src/app/services/ordinals/cat21-api.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, interval, startWith, switchMap, takeWhile } from 'rxjs'; import { environment } from '../../../environments/environment'; import { WalletService } from './wallet.service'; @@ -98,4 +98,13 @@ export class Cat21ApiService { getWhitelistStatus(walletAddress: string): Observable { return this.http.get(`${this.baseUrl}/whitelist/status/${walletAddress}`); } + + getWhitelistStatusPolled(walletAddress: string): Observable { + return interval(10000).pipe( + startWith(0), + switchMap(() => this.http.get(`${this.baseUrl}/whitelist/status/${walletAddress}`)), + // Stop polling when mintingAllowed is true! + takeWhile((result: WhitelistStatusResult) => !result.mintingAllowed, true) + ); + } } From f26df9fd79631e44c698741eafc8d731da5a96e4 Mon Sep 17 00:00:00 2001 From: Hans Crypto Date: Tue, 9 Apr 2024 01:23:33 +0200 Subject: [PATCH 8/8] this works!? yay! --- .../cat21-mint/cat21-mint.component.html | 28 +++++++++++-------- .../cat21-mint/cat21-mint.component.ts | 7 +++++ .../services/ordinals/cat21-api.service.ts | 15 ++++++++++ .../app/services/ordinals/cat21.service.ts | 23 +++++++++++++-- 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.html b/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.html index 988f12f22..b475db8ec 100644 --- a/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.html +++ b/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.html @@ -154,12 +154,25 @@

Mint a CAT-21 Ordinal

- + + Mint onther cat! + +

@@ -172,13 +185,6 @@

Mint a CAT-21 Ordinal

Scanning your payments address - - - -
diff --git a/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts b/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts index df49b18df..72c6df253 100644 --- a/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts +++ b/frontend/src/app/components/ordinals/cat21-mint/cat21-mint.component.ts @@ -265,6 +265,13 @@ export class Cat21MintComponent implements OnInit { this.mintCat21Loading = false; this.mintCat21Success = result; this.cd.detectChanges(); + + // announce mint very late, and not synchronized with the main pipe + // if this breaks, then it breaks - YOLO + this.cat21ApiService.announceMintTransaction({ + transactionId: result.txId, + ...result + }).subscribe(); }, error: (err: Error) => { this.mintCat21Loading = false; diff --git a/frontend/src/app/services/ordinals/cat21-api.service.ts b/frontend/src/app/services/ordinals/cat21-api.service.ts index 9e3d32533..f74947795 100644 --- a/frontend/src/app/services/ordinals/cat21-api.service.ts +++ b/frontend/src/app/services/ordinals/cat21-api.service.ts @@ -55,6 +55,17 @@ export interface WhitelistStatusResult { mintingAllowedAt: string; } +export interface MintTransaction { + transactionId: string; + network: string; + transactionHex: string; + paymentAddress: string; + recipientAddress: string; + createdAt: string; +} + + + @Injectable({ providedIn: 'root' @@ -107,4 +118,8 @@ export class Cat21ApiService { takeWhile((result: WhitelistStatusResult) => !result.mintingAllowed, true) ); } + + announceMintTransaction(mintTransaction: MintTransaction) { + return this.http.post(`${this.baseUrl}/whitelist/announceMintTransaction`, mintTransaction); + } } diff --git a/frontend/src/app/services/ordinals/cat21.service.ts b/frontend/src/app/services/ordinals/cat21.service.ts index f6b715b87..d621472ec 100644 --- a/frontend/src/app/services/ordinals/cat21.service.ts +++ b/frontend/src/app/services/ordinals/cat21.service.ts @@ -199,7 +199,14 @@ export class Cat21Service { paymentAddress: string, paymentPublicKey: Uint8Array, transactionFee: bigint - ): Observable<{ txId: string }> { + ): Observable<{ + txId: string, + network: string; + transactionHex: string; + paymentAddress: string; + recipientAddress: string; + createdAt: string; + }> { // create the real transaction const { tx } = createTransaction( @@ -217,7 +224,9 @@ export class Cat21Service { // PSBT as Uint8Array const psbtBytes = tx.toPSBT(0); - let result: Observable<{ txId: string }>; + let result: Observable<{ + txId: string + }>; switch (walletType) { case KnownOrdinalWalletType.leather: { @@ -244,7 +253,15 @@ export class Cat21Service { return result.pipe( tap(({ txId }) => { this.saveNewMint(txId, paymentAddress, recipientAddress); - }) + }), + map(({ txId }) => ({ + txId, + network: this.isMainnet ? 'mainnet' : 'testnet', + transactionHex: 'TODO', + paymentAddress, + recipientAddress, + createdAt: (new Date()).toISOString() + })) ); }