mirror of
https://github.com/mempool/mempool.git
synced 2025-01-01 03:04:27 +01:00
Merge pull request #879 from mempool/simon/push-tx-form
Broadcast transaction form
This commit is contained in:
commit
39d231bb3c
@ -69,7 +69,8 @@ class Server {
|
|||||||
next();
|
next();
|
||||||
})
|
})
|
||||||
.use(express.urlencoded({ extended: true }))
|
.use(express.urlencoded({ extended: true }))
|
||||||
.use(express.json());
|
.use(express.text())
|
||||||
|
;
|
||||||
|
|
||||||
this.server = http.createServer(this.app);
|
this.server = http.createServer(this.app);
|
||||||
this.wss = new WebSocket.Server({ server: this.server });
|
this.wss = new WebSocket.Server({ server: this.server });
|
||||||
|
@ -768,8 +768,13 @@ class Routes {
|
|||||||
public async $postTransaction(req: Request, res: Response) {
|
public async $postTransaction(req: Request, res: Response) {
|
||||||
res.setHeader('content-type', 'text/plain');
|
res.setHeader('content-type', 'text/plain');
|
||||||
try {
|
try {
|
||||||
const rawtx = Object.keys(req.body)[0];
|
let rawTx;
|
||||||
const txIdResult = await bitcoinApi.$sendRawTransaction(rawtx);
|
if (typeof req.body === 'object') {
|
||||||
|
rawTx = Object.keys(req.body)[0];
|
||||||
|
} else {
|
||||||
|
rawTx = req.body;
|
||||||
|
}
|
||||||
|
const txIdResult = await bitcoinApi.$sendRawTransaction(rawTx);
|
||||||
res.send(txIdResult);
|
res.send(txIdResult);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||||
|
@ -21,12 +21,17 @@ import { TrademarkPolicyComponent } from './components/trademark-policy/trademar
|
|||||||
import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component';
|
import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component';
|
||||||
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
||||||
import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component';
|
import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component';
|
||||||
|
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
|
||||||
|
|
||||||
let routes: Routes = [
|
let routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: MasterPageComponent,
|
component: MasterPageComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: StartComponent,
|
component: StartComponent,
|
||||||
@ -95,6 +100,10 @@ let routes: Routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
component: MasterPageComponent,
|
component: MasterPageComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: StartComponent,
|
component: StartComponent,
|
||||||
@ -164,6 +173,10 @@ let routes: Routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
component: MasterPageComponent,
|
component: MasterPageComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: StartComponent,
|
component: StartComponent,
|
||||||
@ -226,6 +239,10 @@ let routes: Routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
component: MasterPageComponent,
|
component: MasterPageComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: StartComponent,
|
component: StartComponent,
|
||||||
@ -325,6 +342,10 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
|||||||
path: '',
|
path: '',
|
||||||
component: DashboardComponent
|
component: DashboardComponent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'tx/:id',
|
path: 'tx/:id',
|
||||||
component: TransactionComponent
|
component: TransactionComponent
|
||||||
|
@ -57,6 +57,7 @@ import { TrademarkPolicyComponent } from './components/trademark-policy/trademar
|
|||||||
import { StorageService } from './services/storage.service';
|
import { StorageService } from './services/storage.service';
|
||||||
import { HttpCacheInterceptor } from './services/http-cache.interceptor';
|
import { HttpCacheInterceptor } from './services/http-cache.interceptor';
|
||||||
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
||||||
|
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -98,6 +99,7 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
|
|||||||
PrivacyPolicyComponent,
|
PrivacyPolicyComponent,
|
||||||
TrademarkPolicyComponent,
|
TrademarkPolicyComponent,
|
||||||
SponsorComponent,
|
SponsorComponent,
|
||||||
|
PushTransactionComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||||
|
@ -12,6 +12,7 @@ import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.componen
|
|||||||
import { BisqMarketComponent } from './bisq-market/bisq-market.component';
|
import { BisqMarketComponent } from './bisq-market/bisq-market.component';
|
||||||
import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component';
|
import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component';
|
||||||
import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component';
|
import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component';
|
||||||
|
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -30,6 +31,10 @@ const routes: Routes = [
|
|||||||
path: 'market/:pair',
|
path: 'market/:pair',
|
||||||
component: BisqMarketComponent,
|
component: BisqMarketComponent,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'tx/:id',
|
path: 'tx/:id',
|
||||||
component: BisqTransactionComponent
|
component: BisqTransactionComponent
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
<div class="container-xl">
|
||||||
|
<h1 i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</h1>
|
||||||
|
|
||||||
|
<form [formGroup]="pushTxForm" (submit)="pushTxForm.valid && postTx()" novalidate>
|
||||||
|
<div class="mb-3">
|
||||||
|
<textarea formControlName="txHash" class="form-control" rows="5" i18n-placeholder="transaction.hex" placeholder="Transaction Hex"></textarea>
|
||||||
|
</div>
|
||||||
|
<button [disabled]="isLoading" type="submit" class="btn btn-primary mr-2" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</button>
|
||||||
|
<p class="red-color d-inline">{{ error }}</p> <a *ngIf="txId" [routerLink]="['/tx/' | relativeUrl, txId]">{{ txId }}</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,48 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-push-transaction',
|
||||||
|
templateUrl: './push-transaction.component.html',
|
||||||
|
styleUrls: ['./push-transaction.component.scss']
|
||||||
|
})
|
||||||
|
export class PushTransactionComponent implements OnInit {
|
||||||
|
pushTxForm: FormGroup;
|
||||||
|
error: string = '';
|
||||||
|
txId: string = '';
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private apiService: ApiService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.pushTxForm = this.formBuilder.group({
|
||||||
|
txHash: ['', Validators.required],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
postTx() {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.error = '';
|
||||||
|
this.txId = '';
|
||||||
|
this.apiService.postTransaction$(this.pushTxForm.get('txHash').value)
|
||||||
|
.subscribe((result) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.txId = result;
|
||||||
|
this.pushTxForm.reset();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (typeof error.error === 'string') {
|
||||||
|
const matchText = error.error.match('"message":"(.*?)"');
|
||||||
|
this.error = matchText && matchText[1] || error.error;
|
||||||
|
} else if (error.message) {
|
||||||
|
this.error = error.message;
|
||||||
|
}
|
||||||
|
this.isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -148,6 +148,8 @@
|
|||||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||||
|
|
|
|
||||||
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
||||||
|
|
|
||||||
|
<a [routerLink]="['/tx/push' | relativeUrl]" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
||||||
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation, LiquidPegs } from '../interfaces/node-api.interface';
|
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation, LiquidPegs } from '../interfaces/node-api.interface';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { StateService } from './state.service';
|
import { StateService } from './state.service';
|
||||||
@ -104,4 +104,8 @@ export class ApiService {
|
|||||||
listLiquidPegsMonth$(): Observable<LiquidPegs[]> {
|
listLiquidPegsMonth$(): Observable<LiquidPegs[]> {
|
||||||
return this.httpClient.get<LiquidPegs[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month');
|
return this.httpClient.get<LiquidPegs[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postTransaction$(hexPayload: string): Observable<any> {
|
||||||
|
return this.httpClient.post<any>(this.apiBaseUrl + this.apiBasePath + '/api/tx', hexPayload, { responseType: 'text' as 'json'});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user