Merge branch 'master' into natsoni/taptree

This commit is contained in:
mononaut 2025-04-13 10:08:39 +08:00 committed by GitHub
commit 0bbc19926e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 510 additions and 58 deletions

View File

@ -105,8 +105,9 @@ jobs:
--cache-to "type=local,dest=/tmp/.buildx-cache,mode=max" \
--platform linux/amd64,linux/arm64 \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
--build-context rustgbt=./rust \
--build-context backend=./backend \
--output "type=registry,push=true" \
--build-arg commitHash=$SHORT_SHA \
./${{ matrix.service }}/
./${{ matrix.service }}/

View File

@ -11,7 +11,7 @@ to use any trademarks, service marks, logos, or trade names of Mempool Space K.K
or any other contributor to The Mempool Open Source Project.
The Mempool Open Source Project®, Mempool Accelerator®, Mempool Enterprise®,
Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full
Mempool Wallet™, mempool.space®, Be your own explorer™, Explore the full
Bitcoin ecosystem™, Mempool Goggles™, the mempool Logo, the mempool Square Logo,
the mempool block visualization Logo, the mempool Blocks Logo, the mempool
transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo,

View File

@ -17,7 +17,11 @@ class ServicesRoutes {
res.setHeader('Expires', new Date(Date.now() + 1000 * 5).toUTCString());
const walletId = req.params.walletId;
const wallet = await WalletApi.getWallet(walletId);
res.status(200).send(wallet);
if (wallet === null) {
res.status(404).send('No such wallet');
} else {
res.status(200).send(wallet);
}
} catch (e) {
handleError(req, res, 500, 'Failed to get wallet');
}

View File

@ -4,6 +4,7 @@ import { IEsploraApi } from '../bitcoin/esplora-api.interface';
import bitcoinApi from '../bitcoin/bitcoin-api-factory';
import axios from 'axios';
import { TransactionExtended } from '../../mempool.interfaces';
import { promises as fsPromises } from 'fs';
interface WalletAddress {
address: string;
@ -31,16 +32,98 @@ class WalletApi {
private wallets: Record<string, Wallet> = {};
private syncing = false;
private lastSync = 0;
private isSaving = false;
private cacheSchemaVersion = 1;
private static TMP_FILE_NAME = config.MEMPOOL.CACHE_DIR + '/tmp-wallets-cache.json';
private static FILE_NAME = config.MEMPOOL.CACHE_DIR + '/wallets-cache.json';
constructor() {
this.wallets = config.WALLETS.ENABLED ? (config.WALLETS.WALLETS as string[]).reduce((acc, wallet) => {
acc[wallet] = { name: wallet, addresses: {}, lastPoll: 0 };
return acc;
}, {} as Record<string, Wallet>) : {};
// Load cache on startup
if (config.WALLETS.ENABLED) {
this.$loadCache();
}
}
public getWallet(wallet: string): Record<string, WalletAddress> {
return this.wallets?.[wallet]?.addresses || {};
private async $loadCache(): Promise<void> {
try {
const cacheData = await fsPromises.readFile(WalletApi.FILE_NAME, 'utf8');
if (!cacheData) {
return;
}
const data = JSON.parse(cacheData);
if (data.cacheSchemaVersion !== this.cacheSchemaVersion) {
logger.notice('Wallets cache contains an outdated schema version. Clearing it.');
return this.$wipeCache();
}
this.wallets = data.wallets;
// Reset lastSync time to force transaction history refresh
for (const wallet of Object.values(this.wallets)) {
wallet.lastPoll = 0;
for (const address of Object.values(wallet.addresses)) {
address.lastSync = 0;
}
}
logger.info('Restored wallets data from disk cache');
} catch (e) {
logger.warn('Failed to parse wallets cache. Skipping. Reason: ' + (e instanceof Error ? e.message : e));
}
}
private async $saveCache(): Promise<void> {
if (this.isSaving || !config.WALLETS.ENABLED) {
return;
}
try {
this.isSaving = true;
logger.debug('Writing wallets data to disk cache...');
const cacheData = {
cacheSchemaVersion: this.cacheSchemaVersion,
wallets: this.wallets,
};
await fsPromises.writeFile(
WalletApi.TMP_FILE_NAME,
JSON.stringify(cacheData),
{ flag: 'w' }
);
await fsPromises.rename(WalletApi.TMP_FILE_NAME, WalletApi.FILE_NAME);
logger.debug('Wallets data saved to disk cache');
} catch (e) {
logger.warn('Error writing to wallets cache file: ' + (e instanceof Error ? e.message : e));
} finally {
this.isSaving = false;
}
}
private async $wipeCache(): Promise<void> {
try {
await fsPromises.unlink(WalletApi.FILE_NAME);
} catch (e: any) {
if (e?.code !== 'ENOENT') {
logger.err(`Cannot wipe wallets cache file ${WalletApi.FILE_NAME}. Exception ${JSON.stringify(e)}`);
}
}
}
public getWallet(wallet: string): Record<string, WalletAddress> | null {
if (wallet in this.wallets) {
return this.wallets?.[wallet]?.addresses || {};
} else {
return null;
}
}
// resync wallet addresses from the services backend
@ -99,6 +182,9 @@ class WalletApi {
}
wallet.lastPoll = Date.now();
logger.debug(`Synced ${Object.keys(wallet.addresses).length} addresses for wallet ${wallet.name}`);
// Update cache
await this.$saveCache();
} catch (e) {
logger.err(`Error syncing wallet ${wallet.name}: ${(e instanceof Error ? e.message : e)}`);
}

View File

@ -0,0 +1,51 @@
{
"theme": "wiz",
"enterprise": "river",
"branding": {
"name": "river",
"title": "river",
"site_id": 22,
"header_img": "/resources/riverlogo.svg",
"footer_img": "/resources/riverlogo.svg"
},
"dashboard": {
"widgets": [
{
"component": "fees",
"mobileOrder": 4
},
{
"component": "walletBalance",
"mobileOrder": 1,
"props": {
"wallet": "RIVER"
}
},
{
"component": "twitter",
"mobileOrder": 5,
"props": {
"handle": "River"
}
},
{
"component": "wallet",
"mobileOrder": 2,
"props": {
"wallet": "RIVER",
"period": "all"
}
},
{
"component": "blocks"
},
{
"component": "walletTransactions",
"mobileOrder": 3,
"props": {
"wallet": "RIVER"
}
}
]
}
}

View File

@ -460,7 +460,7 @@
Trademark Notice<br>
</div>
<p>
The Mempool Open Source Project&reg;, Mempool Accelerator&reg;, Mempool Enterprise&reg;, Mempool Liquidity&trade;, mempool.space&reg;, Be your own explorer&trade;, Explore the full Bitcoin ecosystem&reg;, Mempool Goggles&trade;, the mempool Logo, the mempool Square Logo, the mempool block visualization Logo, the mempool Blocks Logo, the mempool transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo, the mempool.space Vertical Logo, and the mempool.space Horizontal Logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries.
The Mempool Open Source Project&reg;, Mempool Accelerator&reg;, Mempool Enterprise&reg;, Mempool Wallet&trade;, mempool.space&reg;, Be your own explorer&trade;, Explore the full Bitcoin ecosystem&reg;, Mempool Goggles&trade;, the mempool Logo, the mempool Square Logo, the mempool block visualization Logo, the mempool Blocks Logo, the mempool transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo, the mempool.space Vertical Logo, and the mempool.space Horizontal Logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries.
</p>
<p>
While our software is available under an open source software license, the copyright license does not include an implied right or license to use our trademarks. See our <a href="https://mempool.space/trademark-policy">Trademark Policy and Guidelines</a> for more details, published on &lt;https://mempool.space/trademark-policy&gt;.

View File

@ -499,9 +499,13 @@
} @else if (step === 'googlepay') {
<div id="google-pay-button" class="d-inline-block" style="height: 50px" [style]="loadingGooglePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
} @else if (step === 'cardonfile') {
<div class="paymentMethod mx-2 d-flex justify-content-center align-items-center ml-auto mr-auto" style="width: 200px; height: 55px" (click)="requestCardOnFilePayment()" [style]="loadingCardOnFile ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''">
<fa-icon style="font-size: 24px; color: white" [icon]="['fas', 'credit-card']"></fa-icon>
<span class="ml-2" style="font-size: 22px">{{ estimate?.availablePaymentMethods?.cardOnFile?.card?.brand }} {{ estimate?.availablePaymentMethods?.cardOnFile?.card?.last_4 }}</span>
<div class="ml-auto mr-auto paymentMethod mx-2 d-flex justify-content-center align-items-center" style="width: 200px; height: 55px" (click)="requestCardOnFilePayment()" [style]="loadingCardOnFile ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''">
@if (['VISA', 'MASTERCARD', 'JCB', 'DISCOVER', 'DISCOVER_DINERS', 'AMERICAN_EXPRESS'].includes(estimate?.availablePaymentMethods?.cardOnFile?.card?.brand)) {
<app-svg-images [name]="estimate?.availablePaymentMethods?.cardOnFile?.card?.brand" height="33" class="mr-2"></app-svg-images>
} @else {
<app-svg-images name="OTHER_BRAND" height="33" class="mr-2"></app-svg-images>
}
<span style="font-size: 22px; padding-bottom: 3px">{{ estimate?.availablePaymentMethods?.cardOnFile?.card?.last_4 }}</span>
</div>
}
@if (loadingCashapp || loadingApplePay || loadingGooglePay || loadingCardOnFile) {

View File

@ -61,7 +61,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
@Input() accelerating: boolean = false;
@Input() miningStats: MiningStats;
@Input() eta: ETA;
@Input() scrollEvent: boolean;
@Input() applePayEnabled: boolean = false;
@Input() googlePayEnabled: boolean = true;
@Input() cardOnFileEnabled: boolean = true;
@ -191,9 +190,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.scrollEvent && this.scrollEvent) {
this.scrollToElement('acceleratePreviewAnchor', 'start');
}
if (changes.accelerating && this.accelerating) {
this.moveToStep('success', true);
}
@ -214,6 +210,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
if (this._step === 'checkout') {
this.insertSquare();
this.enterpriseService.goal(8);
this.scrollToElementWithTimeout('acceleratePreviewAnchor', 'start', 100);
}
if (this._step === 'checkout' && this.canPayWithBitcoin) {
this.btcpayInvoiceFailed = false;

View File

@ -86,8 +86,8 @@
<fa-icon [icon]="['fas', 'rocket']" [fixedWidth]="true" i18n-title="master-page.accelerator-dashboard" title="Accelerator Dashboard"></fa-icon>
</a>
</li>
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-pools" *ngIf="stateService.env.MINING_DASHBOARD">
<a class="nav-link" [routerLink]="['/mining' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'hammer']" [fixedWidth]="true" i18n-title="mining.mining-dashboard" title="Mining Dashboard"></fa-icon></a>
<li class="nav-item mining" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-pools" *ngIf="stateService.env.MINING_DASHBOARD">
<a class="nav-link" [routerLink]="['/mining' | relativeUrl]" (click)="collapse()"><div class="svg-wrapper"><app-svg-images name="pickaxe"></app-svg-images></div></a>
</li>
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-lightning" *ngIf="stateService.networkSupportsLightning()">
<a class="nav-link" [routerLink]="['/lightning' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'bolt']" [fixedWidth]="true" i18n-title="master-page.lightning" title="Lightning Explorer"></fa-icon>

View File

@ -26,18 +26,29 @@ li.nav-item {
margin: auto 10px;
padding-left: 10px;
padding-right: 10px;
align-content: center;
&.mining .svg-wrapper {
height: 19.2px;
width: auto;
margin-bottom: 8px;
}
@media (max-width: 992px) {
margin: auto 7px;
margin: 0;
padding-left: 8px;
padding-right: 8px;
&.mining .svg-wrapper {
height: 25.6px;
width: auto;
margin-bottom: 0;
}
}
@media (max-width: 429px) {
margin: auto 5px;
margin: 0;
padding-left: 6px;
padding-right: 6px;
}
@media (max-width: 369px) {
margin: auto 3px;
margin: 0;
padding-left: 4px;
padding-right: 4px;
}

View File

@ -219,7 +219,12 @@
<table class="job-table table table-xs table-borderless table-fixed table-data">
<thead>
<tr>
<th class="data-title clip text-center coinbase" i18n="latest-blocks.coinbasetag">Coinbase tag</th>
<th class="data-title clip text-center coinbase" i18n="latest-blocks.coinbasetag">
<a class="title-link" [routerLink]="['/tx/preview' | relativeUrl]" [fragment]="'offline=true&hex=' + job.coinbase">
Coinbase tag <span>&nbsp;</span>
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon>
</a>
</th>
<th class="data-title clip text-center clean" i18n="next-block.clean">Clean</th>
<th class="data-title clip text-center prevhash" i18n="next-block.prevhash">Prevhash</th>
<th class="data-title clip text-center job-received" i18n="next-block.job-received">Job Received</th>

View File

@ -45,7 +45,9 @@
<app-amount [satoshis]="row.job.reward"></app-amount>
</td>
<td class="height">
{{ row.job.height }}
<a [routerLink]="['/tx/preview' | relativeUrl]" [fragment]="'offline=true&hex=' + row.job.coinbase">
{{ row.job.height }}
</a>
</td>
</tr>
}

View File

@ -1,4 +1,14 @@
<ng-container [ngSwitch]="name">
<ng-container *ngSwitchCase="'pickaxe'">
<svg width="auto" height="100%" viewBox="0 0 576 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2356_1491)">
<path d="M265.74 166.74C267.678 165.026 268.535 159.385 281.735 170.555C293.056 180.134 318.746 203.74 327.461 212.126C344.206 228.239 341.274 231.972 337.7 235.106L133.738 465.761C59.2582 550.625 -51.0751 436.245 37.7716 362.483L265.74 166.74Z" fill="#F1F1F1"/>
<path d="M488.149 346.553C460.764 313.433 399.426 236.909 342.297 183.489C285.168 130.069 193.845 63.1392 159.062 38.1292C144.394 27.5833 159.948 -3.96937 181.477 0.38182C222.567 8.797 284.179 18.2044 344.399 64.8944C360.217 77.1485 371.668 48.3079 386.741 62.3844L441.344 111.176C456.589 125.414 428.859 138.811 441.087 153.956C486.083 209.686 497.252 249.426 526.14 318.036C537.152 350.914 500.974 362.048 488.149 346.553Z" fill="#F1F1F1"/>
</g>
<defs><clipPath id="clip0_2356_1491"><rect width="528" height="502" fill="white"/></clipPath></defs>
</svg>
</ng-container>
<ng-container *ngSwitchCase="'VISA'">
<svg xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" viewBox="0 0 576 512" fill="white" xmlns="http://www.w3.org/2000/svg">
<!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->

View File

@ -1,4 +1,4 @@
<div [formGroup]="timezoneForm" class="text-small text-center">
<div [formGroup]="timezoneForm" class="text-small text-center" style="overflow-x: hidden;">
<select formControlName="mode" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 110px;" (change)="changeMode()">
<option value="local">UTC{{ localTimezoneOffset !== '+0' ? localTimezoneOffset : '' }} {{ localTimezoneName ? '- ' + localTimezoneName : '' }}</option>
<option value="+0" *ngIf="localTimezoneOffset !== '+0'">UTC - Greenwich Mean Time (GMT)</option>

View File

@ -130,7 +130,6 @@
[accelerating]="isAcceleration"
[miningStats]="miningStats"
[eta]="eta"
[scrollEvent]="scrollIntoAccelPreview"
(unavailable)="eligibleForAcceleration = false"
class="w-100"
></app-accelerate-checkout>

View File

@ -121,7 +121,6 @@ export class TrackerComponent implements OnInit, OnDestroy {
acceleratorAvailable: boolean = this.stateService.env.ACCELERATOR && this.stateService.network === '';
eligibleForAcceleration: boolean = false;
accelerationFlowCompleted = false;
scrollIntoAccelPreview = false;
auditEnabled: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true;
enterpriseInfo: any;
@ -735,9 +734,6 @@ export class TrackerComponent implements OnInit, OnDestroy {
return;
}
this.accelerationFlowCompleted = false;
if (this.showAccelerationSummary) {
this.scrollIntoAccelPreview = true;
}
return false;
}

View File

@ -58,7 +58,7 @@
<tr><td>The Mempool Open Source Project</td></tr>
<tr><td>Mempool Accelerator</td></tr>
<tr><td>Mempool Enterprise</td></tr>
<tr><td>Mempool Liquidity</td></tr>
<tr><td>Mempool Wallet</td></tr>
<tr><td>Mempool</td></tr>
<tr><td>mempool.space</td></tr>
<tr><td>Be your own explorer</td></tr>
@ -341,7 +341,7 @@
<p>Also, if you are using our Marks in a way described in the sections "Uses for Which We Are Granting a License," you must include the following trademark attribution at the foot of the webpage where you have used the Mark (or, if in a book, on the credits page), on any packaging or labeling, and on advertising or marketing materials:</p>
<p>"The Mempool Open Source Project&reg;, Mempool Accelerator&reg;, Mempool Enterprise&reg;, Mempool Liquidity&trade;, Mempool&reg;, mempool.space&reg;, Be your own explorer&trade;, Explore the full Bitcoin ecosystem&reg;, Mempool Goggles&trade;, the mempool logo;, the mempool Square logo;, the mempool Blocks logo;, the mempool Blocks 3 | 2 logo;, the mempool.space Vertical Logo;, the Mempool Accelerator logo;, the Mempool Goggles logo;, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries, and are used with permission. Mempool Space K.K. has no affiliation with and does not sponsor or endorse the information provided herein."</p>
<p>"The Mempool Open Source Project&reg;, Mempool Accelerator&reg;, Mempool Enterprise&reg;, Mempool Wallet&trade;, Mempool&reg;, mempool.space&reg;, Be your own explorer&trade;, Explore the full Bitcoin ecosystem&reg;, Mempool Goggles&trade;, the mempool logo;, the mempool Square logo;, the mempool Blocks logo;, the mempool Blocks 3 | 2 logo;, the mempool.space Vertical Logo;, the Mempool Accelerator logo;, the Mempool Goggles logo;, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries, and are used with permission. Mempool Space K.K. has no affiliation with and does not sponsor or endorse the information provided herein."</p>
<li>What to Do When You See Abuse</li>

View File

@ -19,7 +19,11 @@
@if (transaction && !error && !isLoading) {
<div class="title-block">
<h1 i18n="shared.preview-transaction|Preview Transaction">Preview Transaction</h1>
@if (isCoinbase) {
<h1 i18n="shared.preview-coinbase|Preview Coinbase">Preview Coinbase</h1>
} @else {
<h1 i18n="shared.preview-transaction|Preview Transaction">Preview Transaction</h1>
}
<span class="tx-link">
<span class="txid">
@ -39,19 +43,21 @@
<div class="clearfix"></div>
<div class="alert alert-mempool" style="align-items: center;">
<span>
<fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon>
<ng-container *ngIf="!successBroadcast" i18n="transaction.local-tx|This transaction is stored locally in your browser.">
This transaction is stored locally in your browser. Broadcast it to add it to the mempool.
</ng-container>
<ng-container *ngIf="successBroadcast" i18n="transaction.redirecting|Redirecting to transaction page...">
Redirecting to transaction page...
</ng-container>
</span>
<button *ngIf="!successBroadcast" [disabled]="isLoadingBroadcast" type="button" class="btn btn-sm btn-primary btn-broadcast" i18n="transaction.broadcast|Broadcast" (click)="postTx()">Broadcast</button>
<button *ngIf="successBroadcast" type="button" class="btn btn-sm btn-success no-cursor btn-broadcast" i18n="transaction.broadcasted|Broadcasted">Broadcasted</button>
</div>
@if (!isCoinbase) {
<div class="alert alert-mempool" style="align-items: center;">
<span>
<fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon>
<ng-container *ngIf="!successBroadcast" i18n="transaction.local-tx|This transaction is stored locally in your browser.">
This transaction is stored locally in your browser. Broadcast it to add it to the mempool.
</ng-container>
<ng-container *ngIf="successBroadcast" i18n="transaction.redirecting|Redirecting to transaction page...">
Redirecting to transaction page...
</ng-container>
</span>
<button *ngIf="!successBroadcast" [disabled]="isLoadingBroadcast" type="button" class="btn btn-sm btn-primary btn-broadcast" i18n="transaction.broadcast|Broadcast" (click)="postTx()">Broadcast</button>
<button *ngIf="successBroadcast" type="button" class="btn btn-sm btn-success no-cursor btn-broadcast" i18n="transaction.broadcasted|Broadcasted">Broadcasted</button>
</div>
}
@if (!hasPrevouts) {
<div class="alert alert-mempool">

View File

@ -36,7 +36,9 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
isLoadingBroadcast: boolean;
errorBroadcast: string;
successBroadcast: boolean;
isCoinbase: boolean;
broadcastSubscription: Subscription;
fragmentSubscription: Subscription;
isMobile: boolean;
@ViewChild('graphContainer')
@ -77,6 +79,23 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.pushTxForm = this.formBuilder.group({
txRaw: ['', Validators.required],
});
this.fragmentSubscription = this.route.fragment.subscribe((fragment) => {
if (fragment) {
const params = new URLSearchParams(fragment);
const hex = params.get('hex');
if (hex) {
this.pushTxForm.get('txRaw').setValue(hex);
}
const offline = params.get('offline');
if (offline) {
this.offlineMode = offline === 'true';
}
if (this.pushTxForm.get('txRaw').value) {
this.decodeTransaction();
}
}
});
}
async decodeTransaction(): Promise<void> {
@ -184,6 +203,14 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.transaction = tx;
this.rawHexTransaction = hex;
this.isCoinbase = this.transaction.vin[0].is_coinbase;
// Update URL fragment with hex data
this.router.navigate([], {
fragment: this.getCurrentFragments(),
replaceUrl: true
});
this.transaction.flags = getTransactionFlags(this.transaction, this.cpfpInfo, null, null, this.stateService.network);
this.filters = this.transaction.flags ? toFilters(this.transaction.flags).filter(f => f.txPage) : [];
if (this.transaction.sigops >= 0) {
@ -264,6 +291,11 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
resetForm() {
this.resetState();
this.pushTxForm.get('txRaw').setValue('');
this.offlineMode = false;
this.router.navigate([], {
fragment: '',
replaceUrl: true
});
}
@HostListener('window:resize', ['$event'])
@ -306,8 +338,24 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.graphHeight = Math.min(360, this.maxInOut * 80);
}
getCurrentFragments() {
// build a fragment param string from current state
const params = new URLSearchParams();
if (this.offlineMode) {
params.set('offline', 'true');
}
if (this.rawHexTransaction) {
params.set('hex', this.rawHexTransaction);
}
return params.toString();
}
onOfflineModeChange(e): void {
this.offlineMode = !e.target.checked;
this.router.navigate([], {
fragment: this.getCurrentFragments(),
replaceUrl: true
});
}
ngOnDestroy(): void {
@ -315,6 +363,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
this.flowPrefSubscription?.unsubscribe();
this.stateService.markBlock$.next({});
this.broadcastSubscription?.unsubscribe();
this.fragmentSubscription?.unsubscribe();
}
}

View File

@ -87,7 +87,6 @@
[accelerating]="isAcceleration"
[eta]="eta"
[miningStats]="miningStats"
[scrollEvent]="scrollIntoAccelPreview"
[showDetails]="showAccelerationDetails"
[noCTA]="true"
(hasDetails)="setHasAccelerationDetails($event)"

View File

@ -155,7 +155,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
accelerationFlowCompleted = false;
showAccelerationDetails = false;
hasAccelerationDetails = false;
scrollIntoAccelPreview = false;
auditEnabled: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true;
isMempoolSpaceBuild = this.stateService.isMempoolSpaceBuild;
@ -831,7 +830,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
document.location.hash = '#accelerate';
this.openAccelerator();
this.scrollIntoAccelPreview = true;
return false;
}

View File

@ -194,7 +194,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
}
this.transactionsLength = this.transactions.length;
if (!this.txPreview) {
this.cacheService.setTxCache(this.transactions);
}
@ -316,10 +316,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
// Check for address poisoning similarity matches
this.similarityMatches.set(tx.txid, new Map());
const comparableVouts = [
...tx.vout.slice(0, 20),
...this.addresses.map(addr => ({ scriptpubkey_address: addr, scriptpubkey_type: detectAddressType(addr, this.stateService.network) }))
].filter(v => ['p2pkh', 'p2sh', 'v0_p2wpkh', 'v0_p2wsh', 'v1_p2tr'].includes(v.scriptpubkey_type));
const comparableVouts = tx.vout.slice(0, 20).filter(v => ['p2pkh', 'p2sh', 'v0_p2wpkh', 'v0_p2wsh', 'v1_p2tr'].includes(v.scriptpubkey_type));
const comparableVins = tx.vin.slice(0, 20).map(v => v.prevout).filter(v => ['p2pkh', 'p2sh', 'v0_p2wpkh', 'v0_p2wsh', 'v1_p2tr'].includes(v?.scriptpubkey_type));
for (const vout of comparableVouts) {
const address = vout.scriptpubkey_address;

View File

@ -1,5 +1,5 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { switchMap, catchError, map, tap, shareReplay, startWith, scan } from 'rxjs/operators';
import { Address, AddressTxSummary, ChainStats, Transaction } from '@interfaces/electrs.interface';
import { WebsocketService } from '@app/services/websocket.service';
@ -11,6 +11,8 @@ import { seoDescriptionNetwork } from '@app/shared/common.utils';
import { WalletAddress } from '@interfaces/node-api.interface';
import { ElectrsApiService } from '@app/services/electrs-api.service';
import { AudioService } from '@app/services/audio.service';
import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe';
class WalletStats implements ChainStats {
addresses: string[];
@ -134,12 +136,14 @@ export class WalletComponent implements OnInit, OnDestroy {
constructor(
private route: ActivatedRoute,
private router: Router,
private websocketService: WebsocketService,
private stateService: StateService,
private apiService: ApiService,
private electrsApiService: ElectrsApiService,
private audioService: AudioService,
private seoService: SeoService,
private relativeUrlPipe: RelativeUrlPipe,
) { }
ngOnInit(): void {
@ -156,8 +160,11 @@ export class WalletComponent implements OnInit, OnDestroy {
switchMap((walletName: string) => this.apiService.getWallet$(walletName).pipe(
catchError((err) => {
this.error = err;
this.seoService.logSoft404();
console.log(err);
if (err.status === 404) {
this.seoService.logSoft404();
this.router.navigate([this.relativeUrlPipe.transform('')]);
}
return of({});
})
)),

View File

@ -327,6 +327,8 @@ export class StateService {
this.networkChanged$.subscribe((network) => {
this.transactions$ = new BehaviorSubject<TransactionStripped[]>(null);
this.stratumJobs$ = new BehaviorSubject<Record<string, StratumJob>>({});
this.stratumJobUpdate$.next({ state: {} });
this.blocksSubject$.next([]);
});

View File

@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgbCollapseModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle,
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle,
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft,
faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck,
@ -401,7 +401,6 @@ export class SharedModule {
library.addIcons(faClock);
library.addIcons(faTachometerAlt);
library.addIcons(faCubes);
library.addIcons(faHammer);
library.addIcons(faCogs);
library.addIcons(faThList);
library.addIcons(faList);

View File

@ -0,0 +1,45 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>River | Invest in Bitcoin with confidence</title>
<script src="/resources/config.js"></script>
<script src="/resources/customize.js"></script>
<base href="/">
<meta name="description" content="Easily buy Bitcoin in minutes. Zero fees on recurring buys. Invest in Bitcoin with confidence with world-class security." />
<meta property="og:image" content="https://mempool.space/resources/river/river-preview.jpg" />
<meta property="og:image:type" content="image/jpeg" />
<meta property="og:image:width" content="2000" />
<meta property="og:image:height" content="1000" />
<meta property="og:description" content="Easily buy Bitcoin in minutes. Zero fees on recurring buys. Invest in Bitcoin with confidence with world-class security." />
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@mempool">
<meta name="twitter:creator" content="@mempool">
<meta name="twitter:title" content="River | Invest in Bitcoin with confidence">
<meta name="twitter:description" content="Easily buy Bitcoin in minutes. Zero fees on recurring buys. Invest in Bitcoin with confidence with world-class security." />
<meta name="twitter:image" content="https://mempool.space/resources/river/river-preview.jpg" />
<meta name="twitter:domain" content="river.mempool.space">
<link rel="apple-touch-icon" sizes="180x180" href="/resources/river/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/resources/river/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/resources/river/favicons/favicon-16x16.png">
<link rel="manifest" href="/resources/river/favicons/site.webmanifest">
<link rel="shortcut icon" href="/resources/river/favicons/favicon.ico">
<link id="canonical" rel="canonical" href="https://river.mempool.space">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="msapplication-TileColor" content="#000000">
<meta name="msapplication-config" content="/resources/favicons/browserconfig.xml">
<meta name="theme-color" content="#1d1f31">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

View File

@ -0,0 +1,4 @@
<svg width="160" height="40" viewBox="0 0 160 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M75.8865 31.2087H70.2185L66.2029 23.3666H59.531V31.2087H54.5116V8.52042H66.8515C69.4627 8.52042 71.4874 9.22873 72.9256 10.6425C74.3609 12.0591 75.08 13.8144 75.08 15.9082C75.08 17.3135 74.7388 18.5975 74.0592 19.7658C73.3796 20.9341 72.4011 21.8286 71.1293 22.4551L75.8893 31.2059L75.8865 31.2087ZM59.531 18.3407H67.1109C68.0612 18.3407 68.7888 18.1291 69.2964 17.7086C69.8039 17.2881 70.0577 16.7096 70.0577 15.9759C70.0577 15.2422 69.8039 14.6525 69.2964 14.2094C68.7888 13.7664 68.0612 13.5463 67.1109 13.5463H59.531V18.3435V18.3407ZM84.2954 31.2087H79.276V8.52042H84.2954V31.2087ZM99.709 31.0083L106.192 8.52042H111.699L105.135 31.2087H94.0862L87.7949 8.52042H93.3022L99.7119 31.0083H99.709ZM134.718 26.1857V31.2087H114.638V8.52042H134.557V13.5434H119.66V17.3671H134.397V22.3591H119.66V26.1828H134.721L134.718 26.1857ZM159.914 31.2087H154.246L150.23 23.3666H143.559V31.2087H138.539V8.52042H150.879C153.49 8.52042 155.515 9.22873 156.953 10.6425C158.388 12.0591 159.108 13.8144 159.108 15.9082C159.108 17.3135 158.766 18.5975 158.087 19.7658C157.407 20.9341 156.429 21.8286 155.157 22.4551L159.917 31.2059L159.914 31.2087ZM143.559 18.3407H151.138C152.089 18.3407 152.816 18.1291 153.324 17.7086C153.831 17.2881 154.085 16.7096 154.085 15.9759C154.085 15.2422 153.831 14.6525 153.324 14.2094C152.816 13.7664 152.089 13.5463 151.138 13.5463H143.559V18.3435V18.3407Z" fill="#F9F9F9"/>
<path d="M6.50834 35.4049L19.1838 7.60612C19.2346 7.49324 19.4038 7.56097 19.3643 7.67949L10.2673 34.0024C11.6265 33.5734 13.0195 33.2207 14.4407 32.9442L20.481 8.00683C20.5092 7.88549 20.6896 7.92218 20.6727 8.04352L17.1591 32.5124C18.3181 32.3713 19.4968 32.281 20.684 32.2415L21.8289 8.15357C21.8345 8.02941 22.0178 8.02941 22.0235 8.15357L23.1683 32.2415C24.3583 32.2782 25.5342 32.3685 26.6932 32.5124L23.1796 8.04352C23.1627 7.92218 23.3404 7.88549 23.3714 8.00683L29.4116 32.9442C30.8328 33.2207 32.2259 33.5763 33.5851 34.0024L24.4881 7.67949C24.4486 7.56379 24.6178 7.49324 24.6685 7.60612L37.344 35.4049C39.6338 36.3982 41.8107 37.6031 43.8495 39L21.9248 1L0 39C2.03597 37.6031 4.21294 36.3982 6.50552 35.4049" fill="#C5A063"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -43,6 +43,9 @@ CLN_INSTALL=ON
# install UNFURL
UNFURL_INSTALL=ON
# install CKPOOL
CKPOOL_MAINNET_INSTALL=ON
# configure 4 network instances
BITCOIN_MAINNET_ENABLE=ON
BITCOIN_MAINNET_MINFEE_ENABLE=ON
@ -56,6 +59,11 @@ BISQ_MAINNET_ENABLE=ON
ELEMENTS_LIQUID_ENABLE=ON
ELEMENTS_LIQUIDTESTNET_ENABLE=ON
# configure 3 CKPool instances
BITCOIN_MAINNET_CKPOOL=ON
BITCOIN_TESTNET_CKPOOL=ON
BITCOIN_TESTNET4_CKPOOL=ON
# enable lightmode and disable compaction to fit on 1TB SSD drive
BITCOIN_ELECTRS_INSTALL=ON
BITCOIN_ELECTRS_LIGHT_MODE=ON
@ -197,6 +205,18 @@ MEMPOOL_SIGNET_HTTP_PORT=8995
MEMPOOL_LIQUIDTESTNET_HTTP_HOST=127.0.0.1
MEMPOOL_LIQUIDTESTNET_HTTP_PORT=8994
# set CKPool bitcoin mainnet port
CKPOOL_MAINNET_STRATUM_HOST=127.0.0.1
CKPOOL_MAINNET_STRATUM_PORT=3333
# set CKPool bitcoin mainnet port
CKPOOL_TESTNET_STRATUM_HOST=127.0.0.1
CKPOOL_TESTNET_STRATUM_PORT=3334
# set CKPool bitcoin mainnet port
CKPOOL_TESTNET4_STRATUM_HOST=127.0.0.1
CKPOOL_TESTNET4_STRATUM_PORT=3335
##### OS options, should be automatically detected
case $OS in
@ -283,6 +303,11 @@ BITCOIN_GROUP=bitcoin
# bitcoin core data folder, needs about 300GB
BITCOIN_HOME=/bitcoin
# ckpool user/group
CKPOOL_HOME=/ckpool
CKPOOL_USER=ckpool
CKPOOL_GROUP=ckpool
# bitcoin testnet data
BITCOIN_TESTNET_DATA=${BITCOIN_HOME}/testnet3
# bitcoin testnet4 data
@ -367,6 +392,12 @@ BISQ_REPO_BRANCH=master
BISQ_LATEST_RELEASE=master
echo -n '.'
CKPOOL_URL=https://github.com/mempool/ckpool
CKPOOL_REPO_NAME=ckpool
CKPOOL_REPO_BRANCH=master
CKPOOL_LATEST_RELEASE=$(curl -s https://api.github.com/repos/mempool/ckpool/releases/latest|grep tag_name|head -1|cut -d '"' -f4)
echo -n '.'
UNFURL_REPO_URL=https://github.com/mempool/mempool
UNFURL_REPO_NAME=unfurl
UNFURL_REPO_BRANCH=master
@ -421,6 +452,7 @@ FREEBSD_PKG+=(openssh-portable py311-pip rust llvm17 jq base64 libzmq4)
FREEBSD_PKG+=(boost-libs autoconf automake gmake gcc13 libevent libtool pkgconf)
FREEBSD_PKG+=(nginx rsync py311-certbot-nginx mariadb1011-server)
FREEBSD_PKG+=(geoipupdate redis)
FREEBSD_PKG+=(libepoll-shim)
FREEBSD_UNFURL_PKG=()
FREEBSD_UNFURL_PKG+=(nvidia-driver-470 chromium xinit xterm twm ja-sourcehansans-otf)
@ -1113,7 +1145,7 @@ echo "[*] Installing Mempool crontab"
osSudo "${ROOT_USER}" crontab -u "${MEMPOOL_USER}" "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/mempool.crontab"
echo "[*] Installing nvm.sh from GitHub"
osSudo "${MEMPOOL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | zsh'
osSudo "${MEMPOOL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash'
echo "[*] Building NodeJS via nvm.sh"
osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; CC=gcc CXX=g++ nvm install v22.14.0 --shared-zlib'
@ -1223,6 +1255,76 @@ if [ "${BITCOIN_INSTALL}" = ON ];then
osSudo "${ROOT_USER}" sed -i.orig "s/__BITCOIN_RPC_PASS__/${BITCOIN_RPC_PASS}/" "${MINFEE_HOME}/bitcoin.conf"
fi
#######################
# CKPool Installation #
#######################
#CKPool cronjob installer
install_ckpool_cron() {
local network=$1
local network_label=$2
local network_cmd=$3
local port=$4
echo "[*] Installing CKPool ${network_label} Cron for 'ckpool' user"
case $OS in
FreeBSD)
osSudo "${CKPOOL_USER}" sh -c "cd ${CKPOOL_HOME} && (crontab -l > ${TEMP_CRON_FILE} 2>/dev/null || echo \"\" > ${TEMP_CRON_FILE})"
osSudo "${CKPOOL_USER}" sh -c "cd ${CKPOOL_HOME} && echo '@reboot screen -dmS ckpool-${network} sh -c \"while true; do ${CKPOOL_HOME}/${CKPOOL_REPO_NAME}/start ${network_cmd} ${port}; sleep 1; done\"' >> ${TEMP_CRON_FILE}"
osSudo "${CKPOOL_USER}" sh -c "cd ${CKPOOL_HOME} && crontab ${TEMP_CRON_FILE}"
osSudo "${CKPOOL_USER}" sh -c "cd ${CKPOOL_HOME} && rm -f ${TEMP_CRON_FILE}"
;;
Debian)
;;
esac
}
#CKPool binary Installer
if [ "${CKPOOL_INSTALL}" = ON ]; then
echo "[*] Creating 'ckpool' user"
osGroupCreate "${CKPOOL_GROUP}"
osUserCreate "${CKPOOL_USER}" "${CKPOOL_HOME}" "${CKPOOL_GROUP}"
osSudo "${ROOT_USER}" chsh -s `which zsh` "${CKPOOL_USER}"
echo "[*] Creating 'ckpool' data folder"
osSudo "${ROOT_USER}" mkdir -p "${CKPOOL_HOME}"
osSudo "${ROOT_USER}" chown -R "${CKPOOL_USER}:${CKPOOL_GROUP}" "${CKPOOL_HOME}"
osSudo "${CKPOOL_USER}" touch "${CKPOOL_HOME}/.zshrc"
echo "[*] Creating '.ckpool' directory in ckpool user's home to store config files"
osSudo "${ROOT_USER}" mkdir -p "${CKPOOL_HOME}/.ckpool"
osSudo "${ROOT_USER}" chown "${CKPOOL_USER}:${CKPOOL_GROUP}" "${CKPOOL_HOME}/.ckpool"
echo "[*] Cloning Mempool CKPool repo from ${CKPOOL_REPO_URL}"
osSudo "${CKPOOL_USER}" git config --global advice.detachedHead false
osSudo "${CKPOOL_USER}" git clone --branch "${CKPOOL_REPO_BRANCH}" "${CKPOOL_REPO_URL}" "${CKPOOL_HOME}/${CKPOOL_REPO_NAME}"
echo "[*] Checking out CKPOOL ${CKPOOL_LATEST_RELEASE}"
osSudo "${CKPOOL_USER}" sh -c "cd ${CKPOOL_HOME}/${CKPOOL_REPO_NAME} && git checkout ${CKPOOL_LATEST_RELEASE}"
echo "[*] Building CKPool from source repo"
osSudo "${CKPOOL_USER}" sh -c "cd ${CKPOOL_REPO_NAME} && ./autogen.sh --quiet"
osSudo "${CKPOOL_USER}" sh -c "cd ${CKPOOL_REPO_NAME} && ./configure CFLAGS='-O3 -march=native -flto -Wall -I/usr/local/include/libepoll-shim' LDFLAGS='-L/usr/local/lib -lepoll-shim -lpthread'"
osSudo "${CKPOOL_USER}" sh -c "cd ${CKPOOL_REPO_NAME} && gmake -j${NPROC}"
echo "[*] Copy CKPool binarary into OS"
osSudo "${ROOT_USER}" sh -c "cd ${CKPOOL_HOME}/${CKPOOL_REPO_NAME} && cp src/ckpool /usr/local/bin"
TEMP_CRON_FILE="tmp_cron"
if [ "${BITCOIN_MAINNET_CKPOOL}" = ON ]; then
install_ckpool_cron "mainnet" "Mainnet" "bitcoin" 2333
fi
if [ "${BITCOIN_TESTNET_CKPOOL}" = ON ]; then
install_ckpool_cron "testnet" "Testnet" "testnet" 2334
fi
if [ "${BITCOIN_TESTNET4_CKPOOL}" = ON ]; then
install_ckpool_cron "testnet4" "Testnet4" "testnet4" 2335
fi
fi
#########################
# Elements installation #
#########################
@ -1314,7 +1416,7 @@ if [ "${BITCOIN_ELECTRS_INSTALL}" = ON ];then
case $OS in
FreeBSD)
echo "[*] Patching Bitcoin Electrs code for FreeBSD"
osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_HOME}/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sysconf-0.3.4\" && patch -p1 < \"${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/freebsd/sysconf.patch\""
osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_HOME}/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sysconf-0.3.4\" && patch -p1 < \"${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/freebsd/sysconf.patch\""
#osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_ELECTRS_HOME}/src/new_index/\" && sed -i.bak -e s/Snappy/None/ db.rs && rm db.rs.bak"
#osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_ELECTRS_HOME}/src/bin/\" && sed -i.bak -e 's/from_secs(5)/from_secs(1)/' electrs.rs && rm electrs.rs.bak"
;;

View File

@ -131,7 +131,7 @@ export NVM_DIR="${HOME}/.nvm"
source "${NVM_DIR}/nvm.sh"
# what to look for
frontends=(mainnet liquid onbtc bitb meta)
frontends=(mainnet liquid onbtc bitb meta river)
backends=(mainnet testnet testnet4 signet liquid liquidtestnet onbtc bitb)
frontend_repos=()
backend_repos=()

View File

@ -0,0 +1,19 @@
{
"OFFICIAL_MEMPOOL_SPACE": true,
"TESTNET_ENABLED": true,
"TESTNET4_ENABLED": true,
"LIQUID_ENABLED": true,
"LIQUID_TESTNET_ENABLED": true,
"BISQ_ENABLED": true,
"BISQ_SEPARATE_BACKEND": true,
"SIGNET_ENABLED": true,
"MEMPOOL_WEBSITE_URL": "https://mempool.space",
"LIQUID_WEBSITE_URL": "https://liquid.network",
"BISQ_WEBSITE_URL": "https://bisq.markets",
"ITEMS_PER_PAGE": 25,
"LIGHTNING": true,
"ACCELERATOR": true,
"PUBLIC_ACCELERATIONS": true,
"AUDIT": true,
"CUSTOMIZATION": "custom-bitb-config.json"
}

View File

@ -0,0 +1,19 @@
{
"OFFICIAL_MEMPOOL_SPACE": true,
"TESTNET_ENABLED": true,
"TESTNET4_ENABLED": true,
"LIQUID_ENABLED": true,
"LIQUID_TESTNET_ENABLED": true,
"BISQ_ENABLED": true,
"BISQ_SEPARATE_BACKEND": true,
"SIGNET_ENABLED": true,
"MEMPOOL_WEBSITE_URL": "https://mempool.space",
"LIQUID_WEBSITE_URL": "https://liquid.network",
"BISQ_WEBSITE_URL": "https://bisq.markets",
"ITEMS_PER_PAGE": 25,
"LIGHTNING": true,
"ACCELERATOR": true,
"PUBLIC_ACCELERATIONS": true,
"AUDIT": true,
"CUSTOMIZATION": "custom-river-config.json"
}

View File

@ -16,7 +16,7 @@ screen -dmS x startx
sleep 3
# start unfurlers for each frontend
for site in mainnet liquid onbtc bitb meta;do
for site in mainnet liquid onbtc bitb meta river;do
cd "$HOME/${site}/unfurler" && \
echo "starting mempool unfurler: ${site}" && \
screen -dmS "unfurler-${site}" sh -c 'while true;do npm run unfurler;sleep 2;done'

View File

@ -0,0 +1,17 @@
{
"SERVER": {
"HOST": "https://river.tk7.mempool.space",
"HTTP_PORT": 8007
},
"MEMPOOL": {
"HTTP_HOST": "http://127.0.0.1",
"HTTP_PORT": 7,
"NETWORK": "river"
},
"PUPPETEER": {
"CLUSTER_SIZE": 8,
"EXEC_PATH": "/usr/local/bin/chrome",
"MAX_PAGE_AGE": 86400,
"RENDER_TIMEOUT": 3000
}
}

View File

@ -333,6 +333,28 @@ export const networks = {
routes: routes.lightning.routes,
}
}
},
river: {
networkName: 'River',
title: 'River | Invest in Bitcoin with confidence',
description: 'Easily buy Bitcoin in minutes. Zero fees on recurring buys. Invest in Bitcoin with confidence with world-class security.',
fallbackImg: '/resources/river/river-preview.jpg',
routes: { // only dynamic routes supported
block: routes.block,
address: routes.address,
wallet: routes.wallet,
tx: routes.tx,
mining: {
title: "Mining",
routes: {
pool: routes.mining.routes.pool,
}
},
lightning: {
title: "Lightning",
routes: routes.lightning.routes,
}
}
}
};