diff --git a/backend/src/index.ts b/backend/src/index.ts index e65a18ab6..6b1f14efb 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -69,7 +69,8 @@ class Server { next(); }) .use(express.urlencoded({ extended: true })) - .use(express.json()); + .use(express.text()) + ; this.server = http.createServer(this.app); this.wss = new WebSocket.Server({ server: this.server }); diff --git a/backend/src/routes.ts b/backend/src/routes.ts index b1f9a2b15..2836bb5b8 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -768,8 +768,13 @@ class Routes { public async $postTransaction(req: Request, res: Response) { res.setHeader('content-type', 'text/plain'); try { - const rawtx = Object.keys(req.body)[0]; - const txIdResult = await bitcoinApi.$sendRawTransaction(rawtx); + let 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); } catch (e: any) { res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message }) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 511d077e8..0ad66ee2d 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -21,12 +21,17 @@ import { TrademarkPolicyComponent } from './components/trademark-policy/trademar import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component'; import { SponsorComponent } from './components/sponsor/sponsor.component'; import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component'; +import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; let routes: Routes = [ { path: '', component: MasterPageComponent, children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, { path: '', component: StartComponent, @@ -95,6 +100,10 @@ let routes: Routes = [ path: '', component: MasterPageComponent, children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, { path: '', component: StartComponent, @@ -164,6 +173,10 @@ let routes: Routes = [ path: '', component: MasterPageComponent, children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, { path: '', component: StartComponent, @@ -226,6 +239,10 @@ let routes: Routes = [ path: '', component: MasterPageComponent, children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, { path: '', component: StartComponent, @@ -325,6 +342,10 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { path: '', component: DashboardComponent }, + { + path: 'tx/push', + component: PushTransactionComponent, + }, { path: 'tx/:id', component: TransactionComponent diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index de67559b3..bb6383421 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -57,6 +57,7 @@ import { TrademarkPolicyComponent } from './components/trademark-policy/trademar import { StorageService } from './services/storage.service'; import { HttpCacheInterceptor } from './services/http-cache.interceptor'; import { SponsorComponent } from './components/sponsor/sponsor.component'; +import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; @NgModule({ declarations: [ @@ -98,6 +99,7 @@ import { SponsorComponent } from './components/sponsor/sponsor.component'; PrivacyPolicyComponent, TrademarkPolicyComponent, SponsorComponent, + PushTransactionComponent, ], imports: [ BrowserModule.withServerTransition({ appId: 'serverApp' }), diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts index fe99105dd..bebf99867 100644 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ b/frontend/src/app/bisq/bisq.routing.module.ts @@ -12,6 +12,7 @@ import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.componen import { BisqMarketComponent } from './bisq-market/bisq-market.component'; import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component'; +import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; const routes: Routes = [ { @@ -30,6 +31,10 @@ const routes: Routes = [ path: 'market/:pair', component: BisqMarketComponent, }, + { + path: 'tx/push', + component: PushTransactionComponent, + }, { path: 'tx/:id', component: BisqTransactionComponent diff --git a/frontend/src/app/components/push-transaction/push-transaction.component.html b/frontend/src/app/components/push-transaction/push-transaction.component.html new file mode 100644 index 000000000..5762c8363 --- /dev/null +++ b/frontend/src/app/components/push-transaction/push-transaction.component.html @@ -0,0 +1,12 @@ +
+

Broadcast Transaction

+ +
+
+ +
+ +

{{ error }}

{{ txId }} +
+ +
\ No newline at end of file diff --git a/frontend/src/app/components/push-transaction/push-transaction.component.scss b/frontend/src/app/components/push-transaction/push-transaction.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/components/push-transaction/push-transaction.component.ts b/frontend/src/app/components/push-transaction/push-transaction.component.ts new file mode 100644 index 000000000..294f0591a --- /dev/null +++ b/frontend/src/app/components/push-transaction/push-transaction.component.ts @@ -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; + }); + } + +} diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 4554a71a5..72d71b8ad 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -148,6 +148,8 @@ Terms of Service | Privacy Policy + | + Broadcast Transaction diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 48c52547f..306fa9535 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -1,5 +1,5 @@ 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 { Observable } from 'rxjs'; import { StateService } from './state.service'; @@ -104,4 +104,8 @@ export class ApiService { listLiquidPegsMonth$(): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month'); } + + postTransaction$(hexPayload: string): Observable { + return this.httpClient.post(this.apiBaseUrl + this.apiBasePath + '/api/tx', hexPayload, { responseType: 'text' as 'json'}); + } }