diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 0a4a210d1..3d575c6dc 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -1,7 +1,9 @@ import { CpfpInfo, TransactionExtended, TransactionStripped } from '../mempool.interfaces'; import config from '../config'; export class Common { - static nativeAssetId = '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d'; + static nativeAssetId = config.MEMPOOL.NETWORK === 'liquidtestnet' ? + '144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49' + : '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d'; static median(numbers: number[]) { let medianNr = 0; diff --git a/backend/src/config.ts b/backend/src/config.ts index 9513324d8..4c2888834 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -2,7 +2,7 @@ const configFile = require('../mempool-config.json'); interface IConfig { MEMPOOL: { - NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid'; + NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid' | 'liquidtestnet'; BACKEND: 'esplora' | 'electrum' | 'none'; HTTP_PORT: number; SPAWN_CLUSTER_PROCS: number; diff --git a/frontend/.gitignore b/frontend/.gitignore index 98a344c6f..64b7777ea 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -50,6 +50,8 @@ Thumbs.db src/resources/assets.json src/resources/assets.minimal.json +src/resources/assets-testnet.json +src/resources/assets-testnet.minimal.json src/resources/pools.json # environment config diff --git a/frontend/mempool-frontend-config.sample.json b/frontend/mempool-frontend-config.sample.json index 80e8ae4cb..478c3bc55 100644 --- a/frontend/mempool-frontend-config.sample.json +++ b/frontend/mempool-frontend-config.sample.json @@ -2,6 +2,7 @@ "TESTNET_ENABLED": false, "SIGNET_ENABLED": false, "LIQUID_ENABLED": false, + "LIQUID_TESTNET_ENABLED": false, "BISQ_ENABLED": false, "BISQ_SEPARATE_BACKEND": false, "ITEMS_PER_PAGE": 10, diff --git a/frontend/proxy.conf.js b/frontend/proxy.conf.js index 88ad4040b..faae04499 100644 --- a/frontend/proxy.conf.js +++ b/frontend/proxy.conf.js @@ -24,6 +24,7 @@ PROXY_CONFIG = [ '/api/**', '!/api/v1/ws', '!/bisq', '!/bisq/**', '!/bisq/', '!/liquid', '!/liquid/**', '!/liquid/', + '!/liquidtestnet', '!/liquidtestnet/**', '!/liquidtestnet/', '/testnet/api/**', '/signet/api/**' ], target: "https://mempool.space", @@ -57,6 +58,16 @@ PROXY_CONFIG = [ ws: true, secure: false, changeOrigin: true + }, + { + context: ['/api/liquidtestnet**', '/liquidtestnet/api/**'], + target: "https://liquid.network/testnet", + pathRewrite: { + "^/api/liquidtestnet/": "/liquidtestnet/api" + }, + ws: true, + secure: false, + changeOrigin: true } ]; diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index fec2866b2..2e367d78c 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -105,6 +105,91 @@ let routes: Routes = [ }, ], }, + { + path: 'liquidtestnet', + children: [ + { + path: '', + component: MasterPageComponent, + children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, + { + path: '', + component: StartComponent, + children: [ + { + path: '', + component: DashboardComponent + }, + { + path: 'tx/:id', + component: TransactionComponent + }, + { + path: 'block/:id', + component: BlockComponent + }, + { + path: 'mempool-block/:id', + component: MempoolBlockComponent + }, + ], + }, + { + path: 'blocks', + component: LatestBlocksComponent, + }, + { + path: 'graphs', + component: StatisticsComponent, + }, + { + path: 'address/:id', + component: AddressComponent + }, + { + path: 'asset/:id', + component: AssetComponent + }, + { + path: 'assets', + component: AssetsComponent, + }, + { + path: 'docs/api/:type', + component: DocsComponent + }, + { + path: 'docs/api', + redirectTo: 'docs/api/rest' + }, + { + path: 'docs', + redirectTo: 'docs/api/rest' + }, + { + path: 'api', + redirectTo: 'docs/api/rest' + }, + ], + }, + { + path: 'tv', + component: TelevisionComponent + }, + { + path: 'status', + component: StatusViewComponent + }, + { + path: '**', + redirectTo: '' + }, + ] + }, { path: 'liquid', children: [ @@ -466,6 +551,94 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, ], }, + { + path: 'testnet', + component: LiquidMasterPageComponent, + children: [ + { + path: '', + component: StartComponent, + children: [ + { + path: '', + component: DashboardComponent + }, + { + path: 'tx/push', + component: PushTransactionComponent, + }, + { + path: 'tx/:id', + component: TransactionComponent + }, + { + path: 'block/:id', + component: BlockComponent + }, + { + path: 'mempool-block/:id', + component: MempoolBlockComponent + }, + ], + }, + { + path: 'blocks', + component: LatestBlocksComponent, + }, + { + path: 'graphs', + component: StatisticsComponent, + }, + { + path: 'address/:id', + component: AddressComponent + }, + { + path: 'asset/:id', + component: AssetComponent + }, + { + path: 'assets', + component: AssetsComponent, + }, + { + path: 'docs/api/:type', + component: DocsComponent + }, + { + path: 'docs/api', + redirectTo: 'docs/api/rest' + }, + { + path: 'docs', + redirectTo: 'docs/api/rest' + }, + { + path: 'api', + redirectTo: 'docs/api/rest' + }, + { + path: 'about', + component: AboutComponent, + }, + { + path: 'terms-of-service', + component: TermsOfServiceComponent + }, + { + path: 'privacy-policy', + component: PrivacyPolicyComponent + }, + { + path: 'trademark-policy', + component: TrademarkPolicyComponent + }, + { + path: 'sponsor', + component: SponsorComponent, + }, + ], + }, { path: 'tv', component: TelevisionComponent diff --git a/frontend/src/app/assets/assets.component.ts b/frontend/src/app/assets/assets.component.ts index 5f572241d..49c42d76e 100644 --- a/frontend/src/app/assets/assets.component.ts +++ b/frontend/src/app/assets/assets.component.ts @@ -7,6 +7,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { merge, combineLatest, Observable } from 'rxjs'; import { AssetExtended } from '../interfaces/electrs.interface'; import { SeoService } from '../services/seo.service'; +import { StateService } from '../services/state.service'; @Component({ selector: 'app-assets', @@ -15,7 +16,8 @@ import { SeoService } from '../services/seo.service'; changeDetection: ChangeDetectionStrategy.OnPush }) export class AssetsComponent implements OnInit { - nativeAssetId = environment.nativeAssetId; + nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId; + assets: AssetExtended[]; assetsCache: AssetExtended[]; searchForm: FormGroup; @@ -34,6 +36,7 @@ export class AssetsComponent implements OnInit { private route: ActivatedRoute, private router: Router, private seoService: SeoService, + private stateService: StateService, ) { } ngOnInit() { @@ -52,12 +55,22 @@ export class AssetsComponent implements OnInit { take(1), mergeMap(([assets, qp]) => { this.assets = Object.values(assets); - // @ts-ignore - this.assets.push({ - name: 'Liquid Bitcoin', - ticker: 'L-BTC', - asset_id: this.nativeAssetId, - }); + if (this.stateService.network === 'liquid') { + // @ts-ignore + this.assets.push({ + name: 'Liquid Bitcoin', + ticker: 'L-BTC', + asset_id: this.nativeAssetId, + }); + } else if (this.stateService.network === 'liquidtestnet') { + // @ts-ignore + this.assets.push({ + name: 'Test Liquid Bitcoin', + ticker: 'tL-BTC', + asset_id: this.nativeAssetId, + }); + } + this.assets = this.assets.sort((a: any, b: any) => a.name.localeCompare(b.name)); this.assetsCache = this.assets; this.searchForm.get('searchText').enable(); diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index 3280a9da4..890209d38 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -99,7 +99,7 @@ export class AddressComponent implements OnInit, OnDestroy { .pipe( filter((address) => !!address), tap((address: Address) => { - if (this.stateService.network === 'liquid' && /^([m-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) { + if ((this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') && /^([m-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) { this.apiService.validateAddress$(address.address) .subscribe((addressInfo) => { this.addressInfo = addressInfo; diff --git a/frontend/src/app/components/amount/amount.component.html b/frontend/src/app/components/amount/amount.component.html index 6548d28f9..07f669a81 100644 --- a/frontend/src/app/components/amount/amount.component.html +++ b/frontend/src/app/components/amount/amount.component.html @@ -2,12 +2,13 @@ {{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }} - + Confidential ‎{{ satoshis / 100000000 | number : digitsInfo }} L- + tL- t sBTC diff --git a/frontend/src/app/components/asset/asset.component.ts b/frontend/src/app/components/asset/asset.component.ts index e54a3727e..74b074d97 100644 --- a/frontend/src/app/components/asset/asset.component.ts +++ b/frontend/src/app/components/asset/asset.component.ts @@ -20,7 +20,7 @@ import { moveDec } from 'src/app/bitcoin.utils'; }) export class AssetComponent implements OnInit, OnDestroy { network = ''; - nativeAssetId = environment.nativeAssetId; + nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId; asset: Asset; blindedIssuance: boolean; diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html index 427a7d1c3..388497039 100644 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html +++ b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html @@ -10,7 +10,7 @@ - diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss index b36d91ae2..fabb9e0e9 100644 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss +++ b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.scss @@ -102,6 +102,10 @@ nav { background-color: #116761; } +.liquidtestnet.active { + background-color: #494a4a; +} + .testnet.active { background-color: #1d486f; } diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 18fbe5b7c..7995d9d1e 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -81,12 +81,12 @@ Total fees - + - {{ fees * 100000000 | number }} L-sat () +   - + Subsidy + fees: @@ -98,7 +98,7 @@ Total fees - + Subsidy + fees: @@ -125,7 +125,7 @@ Version {{ block.version | decimal2hex }} Taproot - + Bits {{ block.bits | decimal2hex }} @@ -136,7 +136,7 @@ -
+
diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 9df6b5605..3ad905b7f 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -210,7 +210,7 @@ export class BlockComponent implements OnInit, OnDestroy { } setBlockSubsidy() { - if (this.network === 'liquid') { + if (this.network === 'liquid' || this.network === 'liquidtestnet') { this.blockSubsidy = 0; return; } diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 281081876..665822bd5 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -36,6 +36,7 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy { '': ['#9339f4', '#105fb0'], bisq: ['#9339f4', '#105fb0'], liquid: ['#116761', '#183550'], + 'liquidtestnet': ['#494a4a', '#272e46'], testnet: ['#1d486f', '#183550'], signet: ['#6f1d5d', '#471850'], }; @@ -47,7 +48,7 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy { ) { } ngOnInit() { - if (this.stateService.network === 'liquid') { + if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') { this.feeRounding = '1.0-1'; } this.emptyBlocks.forEach((b) => this.emptyBlockStyles.push(this.getStyleForEmptyBlock(b))); diff --git a/frontend/src/app/components/blockchain/blockchain.component.scss b/frontend/src/app/components/blockchain/blockchain.component.scss index fc71bd202..0527798a2 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.scss +++ b/frontend/src/app/components/blockchain/blockchain.component.scss @@ -31,7 +31,7 @@ top: 75px; } -.position-container.liquid { +.position-container.liquid, .position-container.liquidtestnet { left: 420px; } @@ -39,7 +39,7 @@ .position-container { left: 95%; } - .position-container.liquid { + .position-container.liquid, .position-container.liquidtestnet { left: 50%; } .position-container.loading { diff --git a/frontend/src/app/components/docs/api-docs-nav.component.html b/frontend/src/app/components/docs/api-docs-nav.component.html index 63ed8d4e1..38331c5e3 100644 --- a/frontend/src/app/components/docs/api-docs-nav.component.html +++ b/frontend/src/app/components/docs/api-docs-nav.component.html @@ -27,7 +27,7 @@ GET Address Transactions MempoolGET Address UTXO - +

Assets

GET Assets GET Assets Icons @@ -66,7 +66,7 @@ GET Children Pay for Parent GET Transaction GET Transaction Hex -GET Transaction Merkleblock Proof +GET Transaction Merkleblock Proof GET Transaction Merkle Proof GET Transaction Outspend GET Transaction Outspends diff --git a/frontend/src/app/components/docs/api-docs.component.html b/frontend/src/app/components/docs/api-docs.component.html index c41bc57c7..54d0d9109 100644 --- a/frontend/src/app/components/docs/api-docs.component.html +++ b/frontend/src/app/components/docs/api-docs.component.html @@ -24,7 +24,7 @@
-
+
GET Difficulty Adjustment General @@ -228,14 +228,14 @@
Description
-
Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: txid, vout, value, and status (with the status of the funding tx).There is also a valuecommitment field that may appear in place of value, plus the following additional fields: asset/assetcommitment, nonce/noncecommitment, surjection_proof, and range_proof.
+
Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: txid, vout, value, and status (with the status of the funding tx).There is also a valuecommitment field that may appear in place of value, plus the following additional fields: asset/assetcommitment, nonce/noncecommitment, surjection_proof, and range_proof.
-
+
GET Assets Assets @@ -313,7 +313,7 @@
Description
-
Returns details about a block. Available fields: id, height, version, timestamp, bits, nonce, merkle_root, tx_count, size, weight, proof, and previousblockhash.
+
Returns details about a block. Available fields: id, height, version, timestamp, bits, nonce, merkle_root, tx_count, size, weight, proof, and previousblockhash.
@@ -577,7 +577,7 @@
-
+
GET Transaction Merkleblock Proof Transactions
Endpoint
diff --git a/frontend/src/app/components/docs/api-docs.component.ts b/frontend/src/app/components/docs/api-docs.component.ts index 02cca44b2..b46590a89 100644 --- a/frontend/src/app/components/docs/api-docs.component.ts +++ b/frontend/src/app/components/docs/api-docs.component.ts @@ -3663,7 +3663,7 @@ export class ApiDocsComponent implements OnInit { }; this.network$.subscribe((network) => { - this.active = (network === 'liquid') ? 2 : 0; + this.active = (network === 'liquid' || network === 'liquidtestnet') ? 2 : 0; }); } @@ -3679,7 +3679,7 @@ export class ApiDocsComponent implements OnInit { if (network === 'signet') { curlResponse = code.codeSampleSignet.curl; } - if (network === 'liquid') { + if (network === 'liquid' || network === 'liquidtestnet') { curlResponse = code.codeSampleLiquid.curl; } if (network === 'bisq') { diff --git a/frontend/src/app/components/docs/code-template.component.ts b/frontend/src/app/components/docs/code-template.component.ts index 0c46c7a9d..fdd496726 100644 --- a/frontend/src/app/components/docs/code-template.component.ts +++ b/frontend/src/app/components/docs/code-template.component.ts @@ -26,7 +26,7 @@ export class CodeTemplateComponent implements OnInit { if (this.network === 'bisq') { npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-bisq-js`; } - if (this.network === 'liquid') { + if (this.network === 'liquid' || this.network === 'liquidtestnet') { npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-liquid-js`; } return npmLink; @@ -37,7 +37,7 @@ export class CodeTemplateComponent implements OnInit { if (this.network === 'bisq') { npmLink = `https://www.npmjs.org/package/@mempool/bisq.js`; } - if (this.network === 'liquid') { + if (this.network === 'liquid' || this.network === 'liquidtestnet') { npmLink = `https://www.npmjs.org/package/@mempool/liquid.js`; } return npmLink; @@ -50,7 +50,7 @@ export class CodeTemplateComponent implements OnInit { } else { codeText = codeText.replace('%{0}', 'bitcoin'); } - if(['', 'main', 'liquid', 'bisq'].includes(this.network)) { + if(['', 'main', 'liquid', 'bisq', 'liquidtestnet'].includes(this.network)) { codeText = codeText.replace('mempoolJS();', `mempoolJS({ hostname: '${document.location.hostname}' });`); @@ -119,7 +119,7 @@ export class CodeTemplateComponent implements OnInit { if (this.network === 'signet') { codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule); } - if (this.network === 'liquid') { + if (this.network === 'liquid' || this.network === 'liquidtestnet') { codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule); } if (this.network === 'bisq') { @@ -157,7 +157,7 @@ init();`; if (this.network === 'signet') { codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule); } - if (this.network === 'liquid') { + if (this.network === 'liquid' || this.network === 'liquidtestnet') { codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule); } if (this.network === 'bisq') { @@ -237,7 +237,7 @@ yarn add @mempool/liquid.js`; if (this.network === 'signet') { return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleSignet); } - if (this.network === 'liquid') { + if (this.network === 'liquid' || this.network === 'liquidtestnet') { return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleLiquid); } if (this.network === 'bisq') { @@ -259,7 +259,7 @@ yarn add @mempool/liquid.js`; if (this.network === 'signet') { return code.codeSampleSignet.response; } - if (this.network === 'liquid') { + if (this.network === 'liquid' || this.network === 'liquidtestnet') { return code.codeSampleLiquid.response; } if (this.network === 'bisq') { diff --git a/frontend/src/app/components/fees-box/fees-box.component.ts b/frontend/src/app/components/fees-box/fees-box.component.ts index f86ff0b4c..1612c666e 100644 --- a/frontend/src/app/components/fees-box/fees-box.component.ts +++ b/frontend/src/app/components/fees-box/fees-box.component.ts @@ -26,7 +26,7 @@ export class FeesBoxComponent implements OnInit { ) { } ngOnInit(): void { - this.defaultFee = this.stateService.network === 'liquid' ? 0.1 : 1; + this.defaultFee = this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ? 0.1 : 1; this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.feeEstimations$ = this.stateService.mempoolBlocks$ diff --git a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html index f6e02beed..8329fa2fd 100644 --- a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html +++ b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html @@ -1,3 +1,4 @@ +
- + - + - + diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 7cc6a2aec..3dbec5ce3 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -262,7 +262,7 @@ export class DashboardComponent implements OnInit { share(), ); - if (this.stateService.network === 'liquid') { + if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') { this.liquidPegsMonth$ = this.apiService.listLiquidPegsMonth$() .pipe( map((pegs) => { diff --git a/frontend/src/app/services/assets.service.ts b/frontend/src/app/services/assets.service.ts index 4f35532a9..8331ab7d1 100644 --- a/frontend/src/app/services/assets.service.ts +++ b/frontend/src/app/services/assets.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; -import { shareReplay } from 'rxjs/operators'; +import { shareReplay, switchMap } from 'rxjs/operators'; import { StateService } from './state.service'; @Injectable({ @@ -21,8 +21,16 @@ export class AssetsService { apiBaseUrl = this.stateService.env.NGINX_PROTOCOL + '://' + this.stateService.env.NGINX_HOSTNAME + ':' + this.stateService.env.NGINX_PORT; } - this.getAssetsJson$ = this.httpClient.get(apiBaseUrl + '/resources/assets.json').pipe(shareReplay()); - this.getAssetsMinimalJson$ = this.httpClient.get(apiBaseUrl + '/resources/assets.minimal.json').pipe(shareReplay()); - this.getMiningPools$ = this.httpClient.get(apiBaseUrl + '/resources/pools.json').pipe(shareReplay()); + this.getAssetsJson$ = this.stateService.networkChanged$ + .pipe( + switchMap(() => this.httpClient.get(`${apiBaseUrl}/resources/assets${this.stateService.network === 'liquidtestnet' ? '-testnet' : ''}.json`)), + shareReplay(1), + ); + this.getAssetsMinimalJson$ = this.stateService.networkChanged$ + .pipe( + switchMap(() => this.httpClient.get(`${apiBaseUrl}/resources/assets${this.stateService.network === 'liquidtestnet' ? '-testnet' : ''}.minimal.json`)), + shareReplay(1), + ); + this.getMiningPools$ = this.httpClient.get(apiBaseUrl + '/resources/pools.json').pipe(shareReplay(1)); } } diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index 2e1916626..71a57d871 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -29,6 +29,8 @@ export class SeoService { getTitle(): string { if (this.network === 'liquid') return 'mempool - Liquid Network'; + if (this.network === 'liquidtestnet') + return 'mempool - Liquid Network Testnet'; if (this.network === 'bisq') return 'mempool - Bisq Markets'; return 'mempool - ' + (this.network ? this.ucfirst(this.network) : 'Bitcoin') + ' Explorer'; diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 604bb6a87..8d2ee33e6 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -19,6 +19,7 @@ export interface Env { TESTNET_ENABLED: boolean; SIGNET_ENABLED: boolean; LIQUID_ENABLED: boolean; + LIQUID_TESTNET_ENABLED: boolean; BISQ_ENABLED: boolean; BISQ_SEPARATE_BACKEND: boolean; ITEMS_PER_PAGE: number; @@ -38,6 +39,7 @@ const defaultEnv: Env = { 'TESTNET_ENABLED': false, 'SIGNET_ENABLED': false, 'LIQUID_ENABLED': false, + 'LIQUID_TESTNET_ENABLED': false, 'BASE_MODULE': 'mempool', 'BISQ_ENABLED': false, 'BISQ_SEPARATE_BACKEND': false, @@ -116,7 +118,7 @@ export class StateService { this.blocks$ = new ReplaySubject<[Block, boolean]>(this.env.KEEP_BLOCKS_AMOUNT); - if (this.env.BASE_MODULE !== 'mempool') { + if (this.env.BASE_MODULE === 'bisq') { this.network = this.env.BASE_MODULE; this.networkChanged$.next(this.env.BASE_MODULE); } @@ -125,10 +127,10 @@ export class StateService { } setNetworkBasedonUrl(url: string) { - if (this.env.BASE_MODULE !== 'mempool') { + if (this.env.BASE_MODULE !== 'mempool' && this.env.BASE_MODULE !== 'liquid') { return; } - const networkMatches = url.match(/\/(bisq|testnet|liquid|signet)/); + const networkMatches = url.match(/\/(bisq|testnet|liquidtestnet|liquid|signet)/); switch (networkMatches && networkMatches[1]) { case 'liquid': if (this.network !== 'liquid') { @@ -136,6 +138,12 @@ export class StateService { this.networkChanged$.next('liquid'); } return; + case 'liquidtestnet': + if (this.network !== 'liquidtestnet') { + this.network = 'liquidtestnet'; + this.networkChanged$.next('liquidtestnet'); + } + return; case 'signet': if (this.network !== 'signet') { this.network = 'signet'; @@ -144,8 +152,13 @@ export class StateService { return; case 'testnet': if (this.network !== 'testnet') { - this.network = 'testnet'; - this.networkChanged$.next('testnet'); + if (this.env.BASE_MODULE === 'liquid') { + this.network = 'liquidtestnet'; + this.networkChanged$.next('liquidtestnet'); + } else { + this.network = 'testnet'; + this.networkChanged$.next('testnet'); + } } return; case 'bisq': @@ -155,7 +168,12 @@ export class StateService { } return; default: - if (this.network !== '') { + if (this.env.BASE_MODULE !== 'mempool') { + if (this.network !== this.env.BASE_MODULE) { + this.network = this.env.BASE_MODULE; + this.networkChanged$.next(this.env.BASE_MODULE); + } + } else if (this.network !== '') { this.network = ''; this.networkChanged$.next(''); } diff --git a/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts b/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts index a19e93a75..2a6cc4d28 100644 --- a/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts +++ b/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts @@ -11,10 +11,13 @@ export class RelativeUrlPipe implements PipeTransform { ) { } transform(value: string): string { - if (this.stateService.env.BASE_MODULE !== 'mempool') { - return '/' + value; + let network = this.stateService.network; + if (this.stateService.env.BASE_MODULE === 'liquid' && network === 'liquidtestnet') { + network = 'testnet'; + } else if (this.stateService.env.BASE_MODULE !== 'mempool') { + network = ''; } - return (this.stateService.network ? '/' + this.stateService.network : '') + value; + return (network ? '/' + network : '') + value; } } diff --git a/frontend/src/environments/environment.prod.ts b/frontend/src/environments/environment.prod.ts index 5c0708478..ed95312cb 100644 --- a/frontend/src/environments/environment.prod.ts +++ b/frontend/src/environments/environment.prod.ts @@ -1,4 +1,5 @@ export const environment = { production: true, nativeAssetId: '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d', + nativeTestAssetId: '144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49', }; diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index 3dec06541..c7224b679 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -5,6 +5,7 @@ export const environment = { production: false, nativeAssetId: '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d', + nativeTestAssetId: '144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49', }; /* diff --git a/frontend/src/resources/liquidtestnet-logo.png b/frontend/src/resources/liquidtestnet-logo.png new file mode 100644 index 000000000..590016beb Binary files /dev/null and b/frontend/src/resources/liquidtestnet-logo.png differ diff --git a/frontend/sync-assets.js b/frontend/sync-assets.js index 36fb623fd..b1dc4090d 100644 --- a/frontend/sync-assets.js +++ b/frontend/sync-assets.js @@ -42,9 +42,17 @@ if (configContent.BASE_MODULE && configContent.BASE_MODULE === 'liquid') { assetsMinimalJsonUrl = 'https://raw.githubusercontent.com/Blockstream/asset_registry_db/master/index.minimal.json'; } +const testnetAssetsJsonUrl = 'https://raw.githubusercontent.com/Blockstream/asset_registry_testnet_db/master/index.json'; +const testnetAssetsMinimalJsonUrl = 'https://raw.githubusercontent.com/Blockstream/asset_registry_testnet_db/master/index.minimal.json'; + console.log('Downloading assets'); download(PATH + 'assets.json', assetsJsonUrl); console.log('Downloading assets minimal'); download(PATH + 'assets.minimal.json', assetsMinimalJsonUrl); console.log('Downloading mining pools info'); download(PATH + 'pools.json', poolsJsonUrl); +console.log('Downloading testnet assets'); +download(PATH + 'assets-testnet.json', testnetAssetsJsonUrl); +console.log('Downloading testnet assets minimal'); +download(PATH + 'assets-testnet.minimal.json', testnetAssetsMinimalJsonUrl); +
Features @@ -114,7 +114,7 @@ In several hours (or more) - + @@ -124,7 +124,7 @@
Features diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 4f376eb62..2478958f6 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -159,7 +159,7 @@ export class TransactionComponent implements OnInit, OnDestroy { ); }), switchMap((tx) => { - if (this.network === 'liquid') { + if (this.network === 'liquid' || this.network === 'liquidtestnet') { return from(this.liquidUnblinding.checkUnblindedTx(tx)) .pipe( catchError((error) => { diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index c49664b06..7699e2e04 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -48,13 +48,16 @@
- Coinbase (Newly Generated Coins)
{{ vin.scriptsig | hex2ascii }}
+ Coinbase (Newly Generated Coins)
{{ vin.scriptsig | hex2ascii }}
Peg-in P2PK + + {{ vin.issuance ? 'Issuance' : 'UNKNOWN' }} + {{ vin.prevout.scriptpubkey_address | shortenString : 16 }} @@ -244,7 +247,7 @@  
{{ transaction.txid | shortenString : 10 }}ConfidentialConfidential {{ transaction.fee / transaction.vsize | feeRounding }} sat/vB