Introduction
In this article, I will elucidate how to integrate Stratis Blockchain API in custom or third party application. We will create angular application and integrate Stratis Private APIs. We will interact with Stratis Blockchain APIs and show the response in the custom application. Additionally, this article will describe to create user interface to deploy the Smart Contract. This write-up explains and provides the code for all endpoints necessary for deploying the Smart Contract; however, you can get idea and interact with any other endpoints according to your custom software requirement. For more details how to write Smart Contract and generate byte code you can visit here.
Source code is available here
Prerequisites
- IDE for Angular (Visual Studio or Visual Studio Code) as per you
- Stratis FullNode
- Smart Contract Byte Code
For this article, we will be using angular 12, however, it will be same for other version as well.
Create Angular Application
We will create an angular application with angular CLI as shown in below steps.
Install Angular CLI ( if you don’t have)
npm install -g @angular/cli
We will create a new app with following command.
ng new StratisFullNodeWebApp
We will navigate to the project folder and run the application. To build and run the angular application we use serve command as show.
cd StratisFullNodeWebApp
ng serve
Alternatively, to open the default Angular Application: Run below command which build, run and open at once.
ng serve --open
The –open command opens a project to browser at http://localhost:4200/. To design lucrative UI, I have implemented AdminLTE theme. If you want, you can also implement it following steps from here
Create and Add Model in Angular App
Create _Model folder where we will keep all the models for each component.
We can add model using command and also manually as described below: In _model folder right click–> add new item –> Add typescript File and rename it according to your structure.
Let’s Add config.ts file under the model. Where we can keep all the configuration related information and it will be easy to maintain the configuration related information from one place. I am creating this to put the root/base url of api which we call in any services or components. Sample config.ts code:
export const ROOT_URL: string = "http://localhost:38223";
We will integrate the Full Node APIs running at localhost: http://localhost:38223.
I have assumed that you have FullNode already. If you don’t have you can download from here
Open the project and run the Stratis.CirrusMinerD project in devmode. You can use below command:
cd StratisFullNode\src\Stratis.CirrusMinerD
dotnet run -devmode=miner
Open http://localhost:38223/swagger/index.html in your browser and you can see list of APIs.
Wallet Component
To integrate wallet, we will create wallet service and wallet component. Run below command to generate service and component for wallet.
ng g service _service/stratisfullnode/wallet
ng g component apicollection/wallet
In this article, we will implement 3 endpoints from wallet API, however, the implementations of other API(s) will be similar. From the Wallet API Controller, we will call 3 endpoints: Wallet Load, Wallet balance and WalletSplitcoins.
Endpoint: api/Wallet/load: Wallet Load is to make sure private chain is running smoothly and your app is connected with the chain.
Use below credential for Wallet Load
Parameters | Value |
Name | cirrusdev |
Password | password |
Endpoint:/api/Wallet/balance: It gives the lists of Wallet addresses, amount etc. We need wallet address having balance on it to deploy the Smart Contract.
Endpoint: /api/Wallet/splitcoins: It creates requested amount of equal value and gives the list of address with balance. If you get one address having balance and need more addresses, then you can use this to split balance and get more addresses with balance.
To handle API post and response easily, it is better to create model for each of them. Likewise, we will create models for WalletLoad, WalletBalance and WalletSplitcoins. In order to wallet Load, we have to pass parameters: name and password. Therefore, we will create model walletload with name and password properties.
Code of walletload.ts model is given below:
export class WalletLoad {
name: string = "";
password: string = "";
}
Similarly, we will create model for Walletbalance. Code of walletbalance.ts model is:
export class WalletBalance {
walletname: string = "";
accountname: string = "";
includebalancebyaddress: boolean=false;
}
As like above, we will create model For WalletCoinSplit. Code for waletsplitcoin.ts is given below:
export class WalletSplitCoin {
walletName: string = "";
accountName: string = "";
walletPassword: string = "";
totalAmountToSplit: number = 1;
utxosCount: number = 1;
}
Wallet Service
Now, we will create service class for wallet API(s) as wallet.services.ts. We will import Root url from config and use base url to call api endpoints. From the Stratis FullNode swagger page, we can see WalletLoad is post method which needs parameters name and password. Similarly, WalletSplitCoin is also a post, and WalletBalance is get method with parameters. So, we can write code and pass parameters accordingly.
There I am creating 3 methods in this services as shown:
Below is code of wallet.services.ts class for those api endpoints.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ROOT_URL } from '../../_model/config';
@Injectable({
providedIn: 'root'
})
export class WalletService {
constructor(private http: HttpClient) { }
LoadWallet(login:any) {
return this.http.post(ROOT_URL + '/api/Wallet/load', login, { observe: 'response' });
}
WalletBalance(params:any) {
return this.http.get(ROOT_URL + '/api/Wallet/balance', { params: params });
}
WalletSplitCoins(walletSplitCoin: any) {
return this.http.post(ROOT_URL + '/api/Wallet/splitcoins', walletSplitCoin);
}
}
We need HttpClient for http request so import HttpClient in app-module.ts as depicted below, if it is not available there and face error with it.
For routing, please refer to the app-routing.module.ts. After addition of each component we can add it to this app-routing.module.ts.
Wallet Component
Now we will import Wallet Service, all Wallet related models and call methods of service in wallet.component.ts and pass parameters using model respectively.
Code of wallet.component.ts
import { Component, OnInit } from '@angular/core';
import { WalletLoad } from '../../_model/walletload';
import { WalletService } from '../../_service/stratisfullnode/wallet.service';
import { WalletBalance } from '../../_model/walletbalance';
import { WalletSplitCoin } from '../../_model/walletsplitcoins';
import { HttpParams } from '@angular/common/http';
import Swal from 'sweetalert2';
@Component({
selector: 'app-wallet',
templateUrl: './wallet.component.html',
styleUrls: ['./wallet.component.scss']
})
export class WalletComponent implements OnInit {
public login: WalletLoad = new WalletLoad();
public walletbalance: WalletBalance = new WalletBalance();
public walletSplitCoin: WalletSplitCoin = new WalletSplitCoin();
isConnected: boolean = false;
walletInfos: any;
walletbalances: any;
walletSplitedCoins: any;
constructor( private stratisFullNode: WalletService ) { }
IncludeBalanceByAddress: any = ['true', 'false']
ngOnInit(): void {
this.LoadWallet();
}
LoadWallet() {
//this.login.name = "cirrusdev";
//this.login.password = "password";
this.stratisFullNode.LoadWallet(this.login).subscribe((response: any) => {
console.log(response);
if (response.ok) {
this.isConnected = true;
// console.log(response);
Swal.fire('Successful', 'Full Node Connection successful', 'info');
} else {
Swal.fire('Oops...', 'Something went wrong!, Please contact your administrator', 'error');
//alert('Oops...');
(error: any) => {
console.log(error);
}
}
});
}
WalletBalance() {
let params = new HttpParams().set("WalletName", this.walletbalance.walletname).set("AccountName", this.walletbalance.accountname).set("IncludeBalanceByAddress", this.walletbalance.includebalancebyaddress);
this.stratisFullNode.WalletBalance(params).subscribe((response: any) => {
if (response.balances) {
this.walletbalances = response.balances;
//console.log(data);
console.log(this.walletbalances);
} else {
// Swal.fire('Oops...', 'Something went wrong!, Please contact your administrator', 'error');
alert('Opps!!!!');
(error: any) => {
console.log(error);
}
}
});
}
WalletAplitCoins() {
this.stratisFullNode.WalletSplitCoins(this.walletSplitCoin).subscribe((response: any) => {
console.log(response);
if (response.outputs) {
this.walletSplitedCoins = response.outputs;
console.log(this.walletSplitedCoins);
// alert('Load Successfully')
} else {
alert('Oops...');
(error: any) => {
console.log(error);
}
}
});
}
}
Wallet Component Html
In wallet.component.html, we will design forms to pass parameters as user input. It needs one form and submit button for each endpoint. When it gets the response, we are showing the response results just below the submit button. Here is complete code for wallet.component.html page.
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>Stratis FullNode APIs </h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a routerLink="/admin">Wallet</a></li>
<li class="breadcrumb-item active">Load Wallet</li>
</ol>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<form (ngSubmit)="walletLoadForm.form.valid && LoadWallet()" #walletLoadForm="ngForm">
<div class="card card-primary card-outline">
<div class="card-header">
<h3 class="card-title">Wallet Load</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" placeholder="Name" [ngClass]="{ 'is-invalid': walletLoadForm.submitted && name.invalid }" [(ngModel)]="login.name" name="name" #name="ngModel" required>
<div class="text-danger" *ngIf="walletLoadForm.submitted && name.invalid">
<p *ngIf="name.errors?.required">Name is required</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Password</label>
<input type="text" class="form-control" placeholder="Password" [(ngModel)]="login.password" name="type" #type="ngModel" [ngClass]="{ 'is-invalid': walletLoadForm.submitted && type.invalid }" required>
<div class="text-danger" *ngIf="walletLoadForm.submitted && type.invalid">
<p *ngIf="type.errors?.required">Password is required</p>
</div>
</div>
</div>
<div class="col-sm-2">
<button type="submit" class="btn btn-primary"><i class="fa fa-lock"></i> Load Wallet</button>
</div>
<!-- /.col -->
<div class="col-sm-6" *ngIf="isConnected">
<span class="badge badge-success float-sm-right"> Full Node Connection Successful</span>
</div>
</div>
</div>
<!-- /.card-body -->
</div>
</form>
<!--Wallet Balance-->
<form (ngSubmit)="walletBalanceForm.form.valid && WalletBalance()" #walletBalanceForm="ngForm">
<!-- SELECT2 EXAMPLE -->
<div class="card card-primary card-outline">
<div class="card-header">
<h3 class="card-title">Wallet Balance</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Wallet Name</label>
<input type="text" class="form-control" placeholder="Wallet Name" [ngClass]="{ 'is-invalid': walletBalanceForm.submitted && walletname.invalid }" [(ngModel)]="walletbalance.walletname" name="walletname" #walletname="ngModel" required>
<div class="text-danger" *ngIf="walletBalanceForm.submitted && name.invalid">
<p *ngIf="walletname.errors?.required">Wallet Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Account Name</label>
<input type="text" class="form-control" placeholder="Account Name" [(ngModel)]="walletbalance.accountname" name="accountname" #accountname="ngModel" [ngClass]="{ 'is-invalid': walletBalanceForm.submitted && accountname.invalid }" required>
<div class="text-danger" *ngIf="walletBalanceForm.submitted && accountname.invalid">
<p *ngIf="accountname.errors?.required">Account Name is required</p>
</div>
</div>
</div>
<div class="col-sm-4">
<label for="includebalancebyaddress">IncludeBalanceByAddress</label>
<select class="custom-select" [(ngModel)]="walletbalance.includebalancebyaddress" name="includebalancebyaddress">
<option *ngFor="let includebalance of IncludeBalanceByAddress" [ngValue]="includebalance">{{includebalance}}</option>
</select>
</div>
</div>
<div>
</div>
<div class="row">
<div class="col-sm-4">
<button type="submit" class="btn btn-primary"><i class="fa fa-lock"></i> Load Wallet Balance</button>
</div>
<!-- /.col -->
</div>
<div *ngIf="walletbalances" class="card card-primary">
<div class="card-body">
<div class="row">
<table id="appsettingtable" class="table table-striped table- table-bordered">
<thead>
<tr>
<th>Account Name</th>
<th>Path</th>
<th>CoinType</th>
<th>AmountConfirmed</th>
<th>Modified by</th>
<th>SpendableAmount </th>
</tr>
</thead>
<tbody>
<tr *ngFor="let balance of walletbalances">
<td>{{balance.accountName}}</td>
<td>{{balance.accountHdPath}}</td>
<td>{{balance.coinType}}</td>
<td>{{balance.amountConfirmed}}</td>
<td>{{balance.amountUnconfirmed}}</td>
<td>
{{balance.spendableAmount}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- /.card-header -->
<h3 class="card-title">List of Addresses</h3>
<div *ngFor="let balance of walletbalances">
<div *ngFor="let add of balance.addresses">
<span><span class="badge">Address:</span> {{add.address}}</span>
<span><span class="badge">IsUsed:</span> {{add.isUsed}}</span>
<span><span class="badge">IsChange:</span> {{add.isChange}}</span>
<span><span class="badge">Amount Confirmed:</span> {{add.amountConfirmed}}</span>
<span><span class="badge">Amount Unconfirmed:</span> {{add.amountUnconfirmed}}</span>
</div>
</div>
</div>
</div>
<!-- /.card-body -->
</div>
</form>
<!--Wallet Split Coins-->
<form (ngSubmit)="walletSplitCoinForm.form.valid && WalletAplitCoins()" #walletSplitCoinForm="ngForm">
<!-- SELECT2 EXAMPLE -->
<div class="card card-primary card-outline">
<div class="card-header">
<h3 class="card-title">Wallet Split Coins</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Wallet Name</label>
<input type="text" class="form-control" placeholder="Wallet Name" [ngClass]="{ 'is-invalid': walletSplitCoinForm.submitted && walletNameSplitCoin.invalid }" [(ngModel)]="walletSplitCoin.walletName" name="walletName" #walletNameSplitCoin="ngModel" required>
<div class="text-danger" *ngIf="walletSplitCoinForm.submitted && walletNameSplitCoin.invalid">
<p *ngIf="walletNameSplitCoin.errors?.required">Wallet Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Account Name</label>
<input type="text" class="form-control" placeholder="Account Name" [(ngModel)]="walletSplitCoin.accountName" name="accountName" #accountNameSplitCoin="ngModel" [ngClass]="{ 'is-invalid': walletSplitCoinForm.submitted && accountNameSplitCoin.invalid }" required>
<div class="text-danger" *ngIf="walletSplitCoinForm.submitted && accountNameSplitCoin.invalid">
<p *ngIf="accountNameSplitCoin.errors?.required">Account Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Wallet Password</label>
<input type="text" class="form-control" placeholder="Wallet Password" [(ngModel)]="walletSplitCoin.walletPassword" name="walletPassword" #walletpassword="ngModel" [ngClass]="{ 'is-invalid': walletSplitCoinForm.submitted && walletpassword.invalid }" required>
<div class="text-danger" *ngIf="walletSplitCoinForm.submitted && walletpassword.invalid">
<p *ngIf="walletpassword.errors?.required">Wallet Password is required</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Amount to Split</label>
<input type="text" class="form-control" placeholder="No of Aplit" [(ngModel)]="walletSplitCoin.totalAmountToSplit" name="totalAmountToSplit" #totalAmountToSplit="ngModel" [ngClass]="{ 'is-invalid': walletSplitCoinForm.submitted && totalAmountToSplit.invalid }" required>
<div class="text-danger" *ngIf="walletSplitCoinForm.submitted && totalAmountToSplit.invalid">
<p *ngIf="totalAmountToSplit.errors?.required">No of Split is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>No of Split</label>
<input type="text" class="form-control" placeholder="No of Aplit" [(ngModel)]="walletSplitCoin.utxosCount" name="utxosCount" #utxosCount="ngModel" [ngClass]="{ 'is-invalid': walletSplitCoinForm.submitted && utxosCount.invalid }" required>
<div class="text-danger" *ngIf="walletSplitCoinForm.submitted && utxosCount.invalid">
<p *ngIf="utxosCount.errors?.required">No of Split is required</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<button type="submit" class="btn btn-primary"><i class="fa fa-lock"></i> Wallet Split Coins</button>
</div>
<!-- /.col -->
</div>
</div>
<!-- /.card-body -->
</div>
<div *ngIf="walletSplitedCoins" class="card card-primary">
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<table id="appsettingtable" class="table table-striped table- table-bordered">
<thead>
<tr>
<th>Address</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let splitCoin of walletSplitedCoins">
<td>{{splitCoin.address}}</td>
<td>{{splitCoin.amount}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer bg-gradient-green">
Stratis Blockchain.
</div>
</div>
<!-- /.card -->
</form>
</div>
</section>
Output of above code of Wallet, Wallet Load.
Output of Wallet Balance
Output of Wallet Split Coin
Smart Contract Wallet
Similarly, we will implement two API endpoints from SmartContractWallet: WalletCreate and WalletCall. Endpoint: /api/SmartContractWallet/create is used to deploy the Smart Contract using the Bytecode generated from the Sct tool . If we want to change the state of the contract, API endpoint /api/SmartContractWallet/call can be utilized. You can see business case use of it from previous article Voting Contract. These endpoints need parameters to pass, So we will create model for both the endpoints.
Create model as SmartContractWalletCreate. Code of smartcontractwalletcreate.ts model is given below.
export class SmartContractWalletCreate {
walletName: string = "";
accountName: string = "";
outpoints: Array<TransactionRequest> = [];
amount: number = 0;
password: string = "";
feeAmount: string = "";
contractCode: string = "";
gasPrice: number = 10000;
gasLimit: number=250000;
sender: string="";
parameters:Array<string>=[];
}
export class TransactionRequest {
index: number = 0;
transactionId: string = "";
}
Code for smartcontractwalletcall.ts
export class SmartContractWalletCall {
walletName: string = "";
accountName: string = "";
outpoints: Array<TransactionRequest> = [];
contractAddress: string = "";
methodName: string = "";
amount: number = 0;
password: string = "";
feeAmount: string = "";
gasPrice: number = 10000;
gasLimit: number = 250000;
sender: string = "";
parameters: Array<string> = [];
}
export class TransactionRequest {
index: number = 0;
transactionId: string = "";
}
After that, we will create Service and Component for Smart Contract Wallet as shown in angular commands.
ng g service _service/smartcontractwallet --skip-tests
ng g component apicollection/smartcontractwallet --skip-tests
Code for SmartcontractwalletService: smartcontractwallet.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ROOT_URL } from '../../_model/config';
@Injectable({
providedIn: 'root'
})
export class SmartcontractwalletService {
constructor(private http: HttpClient) { }
SmartContractWalletCreate(smartContractWalletCreate: any) {
return this.http.post(ROOT_URL + '/api/SmartContractWallet/create', smartContractWalletCreate);
}
SmartContractWalletCall(smartContractWalletCall: any) {
return this.http.post(ROOT_URL + '/api/SmartContractWallet/call', smartContractWalletCall);
}
}
Similarly, we will import Service and model in component and pass parameters to get response from api endpoints
Code for smartcontractwallet.component.ts
import { Component, OnInit } from '@angular/core';
import { SmartContractWalletCreate } from '../../_model/smartcontractwalletcreate';
import { SmartcontractwalletService } from '../../_service/stratisfullnode/smartcontractwallet.service';
import { HttpParams } from '@angular/common/http';
import { SmartContractWalletCall } from '../../_model/smartcontractwalletcall';
@Component({
selector: 'app-smartcontractwallet',
templateUrl: './smartcontractwallet.component.html',
styleUrls: ['./smartcontractwallet.component.scss']
})
export class SmartcontractwalletComponent implements OnInit {
public smartContractWalletCreate: SmartContractWalletCreate = new SmartContractWalletCreate();
public smartContractWalletCall: SmartContractWalletCall = new SmartContractWalletCall();
constructor(private smartcontractwalletService: SmartcontractwalletService) { }
contractOutputTrnHash: any;
smartcontractWalletCallOutput: any;
parameter: string = "";
testparam: null;
ngOnInit(): void {
}
SmartContractWalletCreate() {
this.smartContractWalletCreate.outpoints = [];
this.smartContractWalletCreate.parameters[0] = this.parameter;
if (this.parameter==="null") {
this.smartContractWalletCreate.parameters = [];
}
// this.smartContractWalletCreate.parameters[0] = this.parameter;
this.smartcontractwalletService.SmartContractWalletCreate(this.smartContractWalletCreate).subscribe((response: any) => {
console.log(response);
if (response) {
this.contractOutputTrnHash = response;
console.log(this.contractOutputTrnHash);
// alert('Load Successfully')
} else {
// Swal.fire('Oops...', 'Something went wrong!, Please contact your administrator', 'error');
alert('Oops...');
(error: any) => {
console.log(error);
}
}
});
}
SmartContractWalletCall() {
this.smartContractWalletCall.outpoints = [];
this.smartContractWalletCall.parameters[0] = this.parameter;
if (this.parameter === "null") {
this.smartContractWalletCall.parameters = [];
}
this.smartcontractwalletService.SmartContractWalletCall(this.smartContractWalletCall).subscribe((response: any) => {
console.log(response);
if (response) {
this.smartcontractWalletCallOutput = response;
console.log(this.smartcontractWalletCallOutput);
// alert('Load Successfully')
} else {
// Swal.fire('Oops...', 'Something went wrong!, Please contact your administrator', 'error');
alert('Oops...');
(error: any) => {
console.log(error);
}
}
});
}
}
HTML page of SmartContractWallet.Component.html page
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>Stratis FullNode APIs </h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<!--<li class="breadcrumb-item"><a routerLink="/admin">Wallet</a></li>
<li class="breadcrumb-item active">Load Wallet</li>-->
</ol>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
<!--SmartContract Wallet Create-->
<form (ngSubmit)="smartContractWalletCreateForm.form.valid && SmartContractWalletCreate()" #smartContractWalletCreateForm="ngForm">
<!-- SELECT2 EXAMPLE -->
<div class="card card-primary card-outline">
<div class="card-header">
<h3 class="card-title">Smart Contract Wallet Create</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Wallet Name</label>
<input type="text" class="form-control" placeholder="Wallet Name" [ngClass]="{ 'is-invalid': smartContractWalletCreateForm.submitted && walletName.invalid }" [(ngModel)]="smartContractWalletCreate.walletName" name="walletName" #walletName="ngModel" required>
<div class="text-danger" *ngIf="smartContractWalletCreateForm.submitted && walletName.invalid">
<p *ngIf="walletName.errors?.required">Wallet Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Account Name</label>
<input type="text" class="form-control" placeholder="Account Name" [(ngModel)]="smartContractWalletCreate.accountName" name="accountName" #accountName="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCreateForm.submitted && accountName.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCreateForm.submitted && accountName.invalid">
<p *ngIf="accountName.errors?.required">Account Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Password</label>
<input type="text" class="form-control" placeholder="Password" [(ngModel)]="smartContractWalletCreate.password" name="password" #password="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCreateForm.submitted && password.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCreateForm.submitted && password.invalid">
<p *ngIf="password.errors?.required">Wallet Password is required</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Amount</label>
<input type="text" class="form-control" placeholder="Amount" [(ngModel)]="smartContractWalletCreate.amount" name="amount" #amount="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCreateForm.submitted && amount.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCreateForm.submitted && amount.invalid">
<p *ngIf="amount.errors?.required">Amount is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Outpoints</label>
<input type="text" class="form-control" placeholder="Outpoints" [(ngModel)]="smartContractWalletCreate.outpoints" name="outpoints" #outpoints="ngModel">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Fee Amount</label>
<input type="text" class="form-control" placeholder="Fee Amount" [(ngModel)]="smartContractWalletCreate.feeAmount" name="feeAmount" #feeAmount="ngModel">
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Contract Code</label>
<input type="text" class="form-control" placeholder="Contract Code" [(ngModel)]="smartContractWalletCreate.contractCode" name="contractCode" #contractCode="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCreateForm.submitted && contractCode.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCreateForm.submitted && contractCode.invalid">
<p *ngIf="contractCode.errors?.required">Contract Code is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Gas Price</label>
<input type="text" class="form-control" placeholder="Gas Price" [(ngModel)]="smartContractWalletCreate.gasPrice" name="gasPrice" #gasPrice="ngModel">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Gas Limit</label>
<input type="text" class="form-control" placeholder="Gas Limit" [(ngModel)]="smartContractWalletCreate.gasLimit" name="gasLimit" #gasLimit="ngModel">
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Sender</label>
<input type="text" class="form-control" placeholder="Sender" [(ngModel)]="smartContractWalletCreate.sender" name="sender" #sender="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCreateForm.submitted && sender.invalid }" required>
<!--<div class="text-danger" *ngIf="smartContractWalletCreateForm.submitted && sender.invalid">
<p *ngIf="sender.errors?.required">Contract Code is required</p>
</div>-->
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Parameters</label>
<input type="text" class="form-control" placeholder="Parameters" [(ngModel)]="parameter" name="parameters" #parameters="ngModel">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<button type="submit" class="btn btn-primary"><i class="fa fa-lock"></i> Create Wallet</button>
</div>
</div>
<div *ngIf="contractOutputTrnHash">
<span>
<span>Transaction Hash: </span> {{contractOutputTrnHash}}
</span>
</div>
</div>
</div>
</form>
<!--SmartContract Wallet Call-->
<form (ngSubmit)="smartContractWalletCallForm.form.valid && SmartContractWalletCall()" #smartContractWalletCallForm="ngForm">
<div class="card card-primary card-outline">
<div class="card-header">
<h3 class="card-title">Smart Contract Wallet Call</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Wallet Name</label>
<input type="text" class="form-control" placeholder="Wallet Name" [ngClass]="{ 'is-invalid': smartContractWalletCallForm.submitted && walletName1.invalid }" [(ngModel)]="smartContractWalletCall.walletName" name="walletName1" #walletName1="ngModel" required>
<div class="text-danger" *ngIf="smartContractWalletCallForm.submitted && walletName1.invalid">
<p *ngIf="walletName1.errors?.required">Wallet Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Account Name</label>
<input type="text" class="form-control" placeholder="Account Name" [(ngModel)]="smartContractWalletCall.accountName" name="accountName1" #accountName1="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCallForm.submitted && accountName1.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCallForm.submitted && accountName1.invalid">
<p *ngIf="accountName1.errors?.required">Account Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Contract Address</label>
<input type="text" class="form-control" placeholder="Contract Address" [(ngModel)]="smartContractWalletCall.contractAddress" name="contractAddress" #contractAddress="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCallForm.submitted && contractAddress.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCallForm.submitted && contractAddress.invalid">
<p *ngIf="contractAddress.errors?.required">Contract Address is required</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Password</label>
<input type="text" class="form-control" placeholder="Password" [(ngModel)]="smartContractWalletCall.password" name="password1" #password1="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCallForm.submitted && password1.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCallForm.submitted && password1.invalid">
<p *ngIf="password1.errors?.required">Wallet Password is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Amount</label>
<input type="text" class="form-control" placeholder="Amount" [(ngModel)]="smartContractWalletCall.amount" name="amount" #amount="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCallForm.submitted && amount.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCallForm.submitted && amount.invalid">
<p *ngIf="amount.errors?.required">Amount is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Outpoints</label>
<input type="text" class="form-control" placeholder="Outpoints" [(ngModel)]="smartContractWalletCall.outpoints" name="outpoints" #outpoints="ngModel">
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Fee Amount</label>
<input type="text" class="form-control" placeholder="Fee Amount" [(ngModel)]="smartContractWalletCall.feeAmount" name="feeAmount" #feeAmount="ngModel">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Gas Price</label>
<input type="text" class="form-control" placeholder="Gas Price" [(ngModel)]="smartContractWalletCall.gasPrice" name="gasPrice" #gasPrice="ngModel">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Gas Limit</label>
<input type="text" class="form-control" placeholder="Gas Limit" [(ngModel)]="smartContractWalletCall.gasLimit" name="gasLimit" #gasLimit="ngModel">
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Sender</label>
<input type="text" class="form-control" placeholder="Sender" [(ngModel)]="smartContractWalletCall.sender" name="sender1" #sender1="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCallForm.submitted && sender1.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCallForm.submitted && sender1.invalid">
<p *ngIf="sender1.errors?.required">Sender is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Method Name</label>
<input type="text" class="form-control" placeholder="Method Name" [(ngModel)]="smartContractWalletCall.methodName" name="methodName" #methodName="ngModel" [ngClass]="{ 'is-invalid': smartContractWalletCallForm.submitted && methodName.invalid }" required>
<div class="text-danger" *ngIf="smartContractWalletCallForm.submitted && methodName.invalid">
<p *ngIf="methodName.errors?.required">Method Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Parameters</label>
<input type="text" class="form-control" placeholder="Parameters" [(ngModel)]="parameter" name="parameters" #parameters="ngModel">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<button type="submit" class="btn btn-primary"><i class="fa fa-lock"></i> Wallet Call</button>
</div>
<!-- /.col -->
</div>
<div *ngIf="smartcontractWalletCallOutput">
<h3 class="card-title">Response output</h3><br />
<span><span class="badge">Fee :</span> {{smartcontractWalletCallOutput.fee}}</span><br />
<span><span class="badge">Hex:</span> {{smartcontractWalletCallOutput.hex}}</span><br />
<span><span class="badge">Message:</span> {{smartcontractWalletCallOutput.message}}</span><br />
<span><span class="badge">Success:</span> {{smartcontractWalletCallOutput.success}}</span><br />
<span><span class="Transaction Id">To:</span> {{smartcontractWalletCallOutput.transactionId}}</span><br />
</div>
</div>
<!-- /.card-body -->
<div class="card-footer bg-gradient-green">
Stratis Blockchain.
</div>
</div>
</form>
Output of above Smart Contract Wallet Create page.
Output UI to interact with Amart Contract Wallet Call (endpoint: /api/SmartContractWallet/call)
Smart Contract Component
From the SmartContract API Controller, we will call endpoint api/SmartContracts/receipt which gives the receipt of transaction. We have to pass transaction hash to get transaction receipt. It gives transaction receipt including Gas Used, From and To address, Logs etc. Additionally, we will integrate endpoint /api/SmartContracts/local-call which creates local call with a contract without a transaction. As we saw in previous article when to use local call and contract call.
We will create Model for GetReceipt and Local-Call
Model code for smartcontractreceipt.ts
export class SmartContractReceipt {
txHash: string = "";
}
Model code for SmartContractLocalCall class.
export class SmartContractLocalCall {
contractAddress: string = "";
methodName: string = "";
amount: string = "";
gasPrice: number = 10000;
gasLimit: number = 250000;
sender: string = "";
parameters: Array<string> = [];
}
Generate Component and Service for Smart Contract API Controller
ng g service _service/smartcontract --skip-tests
ng g component apicollection/smartcontract --skip-tests
Code of SmartContractService class
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ROOT_URL } from '../../_model/config';
@Injectable({
providedIn: 'root'
})
export class SmartcontractService {
constructor(private http: HttpClient) { }
SmartContractReceipt(params: any) {
return this.http.get(ROOT_URL + '/api/SmartContracts/receipt', { params: params });
}
SmartContractLocalCall(smartContractLocalCall: any) {
return this.http.post(ROOT_URL + '/api/SmartContracts/local-call', smartContractLocalCall);
}
}
Code of smartcontract.component.ts
import { Component, OnInit } from '@angular/core';
import { SmartContractReceipt } from '../../_model/smartcontractreceipt';
import { SmartContractLocalCall } from '../../_model/smartcontractlocalcall';
import { SmartcontractService } from '../../_service/stratisfullnode/smartcontract.service';
import { HttpParams } from '@angular/common/http';
@Component({
selector: 'app-smartcontract',
templateUrl: './smartcontract.component.html',
styleUrls: ['./smartcontract.component.scss']
})
export class SmartcontractComponent implements OnInit {
public smartContractReceipt: SmartContractReceipt = new SmartContractReceipt();
public smartContractLocalCall: SmartContractLocalCall = new SmartContractLocalCall();
constructor(private smartcontractService: SmartcontractService) { }
resultGetReceipt: any;
parameter: string = "";
localCallResponseOut: any;
ngOnInit(): void {
}
SmartContractReceipt() {
let params = new HttpParams().set("txHash", this.smartContractReceipt.txHash);
this.smartcontractService.SmartContractReceipt(params).subscribe((response: any) => {
if (response) {
this.resultGetReceipt = response;
console.log(this.resultGetReceipt);
} else {
alert('Opps!!!!');
(error: any) => {
console.log(error);
}
}
});
}
SmartContractForLocalCall() {
this.smartContractLocalCall.parameters= []; this.smartcontractService.SmartContractLocalCall(this.smartContractLocalCall).subscribe((response: any) => {
console.log(response);
if (response) {
this.localCallResponseOut = response;
console.log(this.localCallResponseOut);
} else {
alert('Oops...');
(error: any) => {
console.log(error);
}
}
});
}
}
HTML for smartcontract.component.html
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>Stratis FullNode APIs </h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
</ol>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
<!--Get Receipt-->
<form (ngSubmit)="smartcontractGetReceiptForm.form.valid && SmartContractReceipt()" #smartcontractGetReceiptForm="ngForm">
<!-- SELECT2 EXAMPLE -->
<div class="card card-primary card-outline">
<div class="card-header">
<h3 class="card-title">Smart Contract Receipt</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Transaction Hash </label>
<input type="text" class="form-control" placeholder="Transaction Hash" [ngClass]="{ 'is-invalid': smartcontractGetReceiptForm.submitted && txHash.invalid }" [(ngModel)]="smartContractReceipt.txHash" name="txHash" #txHash="ngModel" required>
<div class="text-danger" *ngIf="smartcontractGetReceiptForm.submitted && txHash.invalid">
<p *ngIf="txHash.errors?.required">Transaction Hash is required</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<button type="submit" class="btn btn-primary"><i class="fa fa-lock"></i> Get Receipt</button>
</div>
<!-- /.col -->
</div>
<!-- /.card-body -->
<div *ngIf="resultGetReceipt">
<h3 class="card-title">Response output</h3><br />
<span><span class="badge">Transaction Hash:</span> {{resultGetReceipt.transactionHash}}</span><br />
<span><span class="badge">Block Hash:</span> {{resultGetReceipt.blockHash}}</span><br />
<span><span class="badge">PostState:</span> {{resultGetReceipt.postState}}</span><br />
<span><span class="badge">From:</span> {{resultGetReceipt.from}}</span><br />
<span><span class="badge">To:</span> {{resultGetReceipt.to}}</span><br />
<span><span class="badge">GasUsed:</span> {{resultGetReceipt.gasUsed}}</span><br />
<span><span class="badge">New Contract Address:</span> {{resultGetReceipt.newContractAddress}}</span><br />
<span><span class="badge">Success:</span> {{resultGetReceipt.success}}</span><br />
<span><span class="badge">Return Value:</span> {{resultGetReceipt.returnValue}}</span><br />
<span><span class="badge">Bloom:</span> {{resultGetReceipt.bloom}}</span><br />
<span><span class="badge">Error:</span> {{resultGetReceipt.error}}</span><br />
<span><span class="badge">Log:</span> {{resultGetReceipt.logs}}</span><br />
</div>
</div>
</div>
</form>
<form (ngSubmit)="smartcontractLocalCallForm.form.valid && SmartContractForLocalCall()" #smartcontractLocalCallForm="ngForm">
<!-- SELECT2 EXAMPLE -->
<div class="card card-primary card-outline">
<div class="card-header">
<h3 class="card-title">Smart Contract Local Call</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Contract Address </label>
<input type="text" class="form-control" placeholder="Contract Address" [ngClass]="{ 'is-invalid': smartcontractLocalCallForm.submitted && contractAddress.invalid }" [(ngModel)]="smartContractLocalCall.contractAddress" name="contractAddress" #contractAddress="ngModel" required>
<div class="text-danger" *ngIf="smartcontractLocalCallForm.submitted && contractAddress.invalid">
<p *ngIf="contractAddress.errors?.required">Contract Address is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Method Name </label>
<input type="text" class="form-control" placeholder="Method Name" [ngClass]="{ 'is-invalid': smartcontractLocalCallForm.submitted && methodName.invalid }" [(ngModel)]="smartContractLocalCall.methodName" name="methodName" #methodName="ngModel" required>
<div class="text-danger" *ngIf="smartcontractLocalCallForm.submitted && methodName.invalid">
<p *ngIf="methodName.errors?.required">Method Name is required</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Amount</label>
<input type="text" class="form-control" placeholder="Amount" [(ngModel)]="smartContractLocalCall.amount" name="amount" #amount="ngModel" [ngClass]="{ 'is-invalid': smartcontractLocalCallForm.submitted && amount.invalid }" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Gas Price</label>
<input type="text" class="form-control" placeholder="Gas Price" [(ngModel)]="smartContractLocalCall.gasPrice" name="gasPrice" #gasPrice="ngModel">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Gas Limit</label>
<input type="text" class="form-control" placeholder="Gas Limit" [(ngModel)]="smartContractLocalCall.gasLimit" name="gasLimit" #gasLimit="ngModel">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Sender</label>
<input type="text" class="form-control" placeholder="Sender" [(ngModel)]="smartContractLocalCall.sender" name="sender" #sender="ngModel">
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Parameters</label>
<input type="text" class="form-control" placeholder="Parameters" [(ngModel)]="parameter" name="parameters" #parameters="ngModel">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<button type="submit" class="btn btn-primary"><i class="fa fa-lock"></i> Make Local Call</button>
</div>
</div>
<!-- /.card-body -->
<div *ngIf="localCallResponseOut">
<h3 class="card-title">Response output</h3><br />
<span><span class="badge">Internal Transfer:</span> {{localCallResponseOut.internalTransfers}}</span><br />
<span><span class="badge">GasConsumed Value:</span> {{localCallResponseOut.gasConsumed.value}}</span><br />
<span><span class="badge">Revert:</span> {{localCallResponseOut.revert}}</span><br />
<span><span class="badge">Error Message:</span> {{localCallResponseOut.errorMessage}}</span><br />
<span><span class="badge">Return:</span> {{localCallResponseOut.return}}</span><br />
<span><span class="badge">Log:</span> {{localCallResponseOut.logs}}</span><br />
</div>
</div>
</div>
<div class="card-footer bg-gradient-green">
Stratis Blockchain.
</div>
<!-- /.card -->
</form>
Out put of Smart Contract Get Receipt UI
Smart Contract Local call UI
Summary
Hence, we can integrate Stratis API in any custom application. In this article, we have implemented Stratis private blockchain in our own application through which we can deploy the Smart Contract. Additionally, write-up has shown how can you manage the API get, post and response model as well as segregate services, models for APIs. The article has explained the complete steps to develop components with user interface to validate Startis API with input and response formatted output. Furthermore, you can take idea from this and integrate Stratis Blockchain any API endpoint to your custom application based on your application need. I have developed an angular application for complete demonstration which you can get from repository.
[…] article is an extension of the previous article Integrate Stratis Blockchain APIs In Your Custom Application. The article describes how to integrate Stratis blockchain transaction APIs in your custom […]