Commit ce39a68a authored by XFT-dev's avatar XFT-dev
Browse files

initializing repo

parent 8605a110
<router-outlet></router-outlet>
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});
import {Component, OnInit} from '@angular/core';
import {action} from 'mobx-angular';
import {AppService} from './app.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
constructor(private appService: AppService) {}
@action ngOnInit() {
this.appService.init();
}
}
import {BrowserModule, HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { environment } from '../environments/environment';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {HttpClient, HttpClientModule} from '@angular/common/http';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {TranslateHttpLoader} from '@ngx-translate/http-loader';
import {MobxAngularModule} from 'mobx-angular';
import { ServiceWorkerModule } from '@angular/service-worker';
// modules
import { AppRoutingModule } from './app-routing.module';
import {IntegrationsModule} from './integrations/integrations.module';
// components
import {HeaderComponent} from './components/header/header.component';
import { AppComponent } from './app.component';
import {LoginComponent} from './pages/login/login.component';
import {SharedModule} from './shared/shared.module';
import {FormComponent} from './pages/form/form.component';
// directives
import {NumberOnlyDirective} from './shared/directives/number-only/number-only.directive';
import {AngularWeb3RecentTransactionsService} from 'angular-web3-components';
import {GestureConfig} from '@angular/material/core';
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
declarations: [
AppComponent,
FormComponent,
HeaderComponent,
NumberOnlyDirective,
LoginComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
HttpClientModule,
IntegrationsModule,
SharedModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
}),
ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}),
],
exports: [
SharedModule
],
providers: [{ provide: HAMMER_GESTURE_CONFIG, useClass: GestureConfig }, AngularWeb3RecentTransactionsService],
bootstrap: [AppComponent]
})
export class AppModule { }
import {Injectable} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {AuthService} from './core/services/auth/auth.service';
import {TransactionsService} from './core/services/services/transactions.service';
@Injectable({
providedIn: 'root'
})
export class AppService {
constructor(
private translate: TranslateService,
private transactionsService: TransactionsService,
private authService: AuthService,
) { }
init() {
this.authService.init();
this.translate.setDefaultLang('en');
this.translate.use('en');
this.transactionsService.init();
}
}
<ng-container *mobxAutorun>
<div class="header-box">
<div class="header-box__logo">
<a routerLink="/">
<img [src]="'assets/img/logo-icon.svg'" alt="logo"/>
</a>
</div>
<div class="display-flex">
<div *ngIf="isLogged" class="header-box__logo__status">
<ng-web3-recent-transactions
[web3]="web3"
[storageService]="storageService"
[accountAddress]="accountAddress"
[applicationName]="applicationName">
</ng-web3-recent-transactions>
</div>
<div class="header-box__logout-icon" [matTooltip]="'Back to origin site'"
aria-label="Back to origin site">
<a href="https://www.offshift.io/">
<img [src]="'assets/img/logout.svg'" alt="logo"/>
</a>
</div>
</div>
</div>
</ng-container>
@import "./src/themes/variables";
.display-flex{
display: flex;
justify-content: center;
align-items: center;
}
.header-box{
height: 90px;
background-color: $white-color;
display: flex;
justify-content: space-between;
align-items: center;
&__logo{
img{
width: 160px;
padding-left: 25px;
}
&__status{
margin-right: 20px;
}
}
&__logout-icon{
img{
width: 30px;
padding-right: 25px;
}
}
//&__menu-list {
// display: flex;
// height: 90px;
// align-items: flex-end;
// margin-right: 30px;
// &__item{
// color: $black-color;
// font-style: normal;
// font-size: 16px;
// line-height: 20px;
// text-align: center;
// font-weight: 600;
// white-space: nowrap;
// text-transform: uppercase;
// margin: 0 20px;
// padding-bottom: 20px;
// &__active{
// padding-bottom: 16px;
// border-bottom: solid 4px $form-color;
// }
// }
//}
}
import {Component, OnInit} from '@angular/core';
import {WEB3} from '../../integrations/dictionaries/meta-mask.dictionary';
import {LocalStorageService} from '../../core/services/services/storage.service';
import {AuthService} from '../../core/services/auth/auth.service';
import {computed} from 'mobx-angular';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent implements OnInit {
public web3 = window[WEB3];
public accountAddress: string;
public applicationName: string;
constructor(public storageService: LocalStorageService,
private authService: AuthService) {}
ngOnInit() {
this.accountAddress = this.authService.user?.address;
this.applicationName = 'offshift';
}
@computed get isLogged() {
return this.authService.isLogged;
}
}
import {Injectable} from '@angular/core';
import {CanActivate, Router, RouterStateSnapshot, ActivatedRouteSnapshot} from '@angular/router';
import {action} from 'mobx-angular';
import {AuthService} from '../../services/auth/auth.service';
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private authenticationService: AuthService
) { }
@action canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (!this.authenticationService.isLogged) {
this.router.navigate(['/login']);
return false;
}
return true;
}
}
import {Injectable} from '@angular/core';
import {observable, action, computed} from 'mobx-angular';
import {Router} from '@angular/router';
import {Web3Service} from '../../../integrations/services/web3/web3.service';
import { LOCAL_STORAGE_ADDRESS_KEY} from '../../../integrations/dictionaries/meta-mask.dictionary';
import {UtilsService} from '../services/utils.service';
export interface User {
address: string;
}
@Injectable({
providedIn: 'root'
})
export class AuthService {
@observable user: User;
constructor(private web3: Web3Service,
private utilsService: UtilsService,
private router: Router) {}
@action async init() {
this.user = this.getUserLoggedIn;
if ( this.user) {
this.setUserLoggedIn(this.user);
await this.web3.setMetamaskProvider();
} else {
this.web3.setInfuraProvider().then();
}
}
@action connectToMetaMask() {
this.web3.connectToMetaMask().subscribe( res => {
this.setUserLoggedIn({address: res});
this.router.navigate(['/']).then();
});
}
@action setUserLoggedIn(user: User) {
this.user = user;
localStorage.setItem(LOCAL_STORAGE_ADDRESS_KEY, JSON.stringify(user));
console.log('saved on localStorage');
}
@computed get getUserLoggedIn(): User {
if (localStorage.getItem(LOCAL_STORAGE_ADDRESS_KEY)) {
return JSON.parse(localStorage.getItem(LOCAL_STORAGE_ADDRESS_KEY));
} else {
return null;
}
}
@action logout() {
this.removeUser();
this.web3.setInfuraProvider().then();
}
@action removeUser() {
window.localStorage.removeItem(LOCAL_STORAGE_ADDRESS_KEY);
this.router.navigate(['/']).then();
this.user = null;
}
@computed get isLogged(): boolean {
return this.user != null;
}
}
import {Injectable} from '@angular/core';
import {Contract} from 'web3-eth-contract';
import {Subject} from 'rxjs';
import {Web3Service} from '../../../integrations/services/web3/web3.service';
import {UtilsService} from './utils.service';
import {BLOCKS_PER_DAY, DIVIDER_FOR_BALANCE, PID, STAKING_CONTRACT_ADDRESS_MAIN_NET, TOKEN_DECIMALS, WEB3, XFT_APY_CONST} from '../../../integrations/dictionaries/meta-mask.dictionary';
import {ONSEN_CONTRACT_ADDRESS_MAIN_NET} from '../../../integrations/dictionaries/meta-mask.dictionary';
import sushiData from '@sushiswap/sushi-data';
import {AngularWeb3RecentTransactionsService} from 'angular-web3-components';
@Injectable({
providedIn: 'root'
})
export class StakeSlpService {
private slpBalance: string;
public slpBalanceChange = new Subject<string>();
private staked: string;
public stakedChange = new Subject<string>();
private slpAllowance: string;
public slpAllowanceChange = new Subject<string>();
private rewardsXFT: number;
public rewardsXFTChange = new Subject<number>();
private rewardsSushi: number;
public rewardsSushiChange = new Subject<number>();
private apySushi: any;
public apySushiChange = new Subject<number>();
private apyXFT: any;
public apyXFTChange = new Subject<number>();
constructor(
private web3Service: Web3Service,
private utilsService: UtilsService,
private recentTransactionsService: AngularWeb3RecentTransactionsService,
) {}
public getSLPBalance(accountAddress: string): void {
if (this.slpBalance) {
this.slpBalanceChange.next(this.slpBalance);
} else {
this.fetchSLPBalance(accountAddress);
}
}
public fetchSLPBalance(accountAddress: string): void {
this.web3Service.getSlpContract().then((contract: Contract) => {
contract.methods.balanceOf(accountAddress).call({from: accountAddress}).then((res: string) => {
this.slpBalance = res;
this.slpBalanceChange.next(this.slpBalance);
});
});
}
public getStaked(accountAddress: string): void {
if (this.staked) {
this.stakedChange.next(this.staked);
} else {
this.fetchStaked(accountAddress);
}
}
public fetchStaked(accountAddress: string): void {
this.web3Service.getStakingContract().then((contract: Contract) => {
contract.methods.balanceOf(accountAddress).call({from: accountAddress}).then((res: string) => {
this.staked = res;
this.stakedChange.next(this.staked);
});
});
}
public getSLPAllowance(accountAddress: string): void {
if (this.slpAllowance) {
this.slpAllowanceChange.next(this.slpAllowance);
} else {
this.fetchSLPAllowance(accountAddress);
}
}
public fetchSLPAllowance(accountAddress: string): void {
this.web3Service.getSlpContract().then((contract: Contract) => {
contract.methods.allowance(accountAddress, STAKING_CONTRACT_ADDRESS_MAIN_NET).call({from: accountAddress}).then((res: string) => {
this.slpAllowance = res;
this.slpAllowanceChange.next(this.slpAllowance);
});
});
}
public approveNewAllowance(amount: string, accountAddress: string): Promise<any> {
return this.web3Service.getSlpContract().then((contract: Contract) => {
return contract.methods.approve(
STAKING_CONTRACT_ADDRESS_MAIN_NET,
amount
).send({from: accountAddress}, (err, hash) => {
if (!err) {
this.recentTransactionsService.saveTransaction('Approve new allowance for stake SLP', hash);
}
});
});
}
public stake(amount: string, accountAddress: string): Promise<any> {
return new Promise<any>((resolve, reject) => {
this.web3Service.getStakingContract().then((contract: Contract) => {
contract.methods.stake(amount).send({from: accountAddress}, (err, hash) => {
if (!err) {
this.recentTransactionsService.saveTransaction('Stake SLP', hash);
resolve(hash);
} else {
reject(err);
}
});
});
});
}
public unStake(amount: string, accountAddress: string): Promise<any> {
return new Promise<any>((resolve, reject) => {
this.web3Service.getStakingContract().then((contract: Contract) => {
contract.methods.withdraw(amount).send({from: accountAddress}, (err, hash) => {
if (!err) {
this.recentTransactionsService.saveTransaction('Withdraw SLP', hash);
resolve(hash);
} else {
reject(err);
}
});
});
});
}
public claimReward(accountAddress: string): Promise<any> {
return new Promise<any>((resolve, reject) => {
return this.web3Service.getStakingContract().then((contract: Contract) => {
contract.methods.getReward().send({from: accountAddress}, (err, hash) => {
if (!err) {
this.recentTransactionsService.saveTransaction('Get reward', hash);
resolve(hash);
} else {
reject(err);
}
});
});
});
}
public getRewardsXFT(accountAddress: string): void {
if (this.rewardsXFT) {
this.rewardsXFTChange.next(this.rewardsXFT);
} else {
this.fetchRewardsXFT(accountAddress);
}
}
public fetchRewardsXFT(accountAddress: string): void {
this.web3Service.getStakingContract().then((contract: Contract) => {
contract.methods.earnedXFT(accountAddress).call({from: accountAddress}).then((res: string) => {
this.rewardsXFT = this.utilsService.parseAmount(res, DIVIDER_FOR_BALANCE);
this.rewardsXFT = this.rewardsXFT / 100;
this.rewardsXFTChange.next(this.rewardsXFT);
});
});
}
public getRewardsSushi(accountAddress: string): void {
if (this.rewardsSushi) {
this.rewardsSushiChange.next(this.rewardsSushi);
} else {
this.fetchRewardsSushi(accountAddress);
}
}
public fetchRewardsSushi(accountAddress: string): void {
this.web3Service.getStakingContract().then((contract: Contract) => {
contract.methods.earnedSushi(accountAddress).call({from: accountAddress}).then((res: string) => {
this.rewardsSushi = this.utilsService.parseAmount(res, DIVIDER_FOR_BALANCE);
this.rewardsSushi = this.rewardsSushi / 100;
this.rewardsSushiChange.next(this.rewardsSushi);
});
});
}
public getAPYSushi(accountAddress: string): void {
if (this.apySushi) {
this.apySushiChange.next(this.apySushi);
} else {
this.fetchAPYSushi(accountAddress).then();
}
}
public async fetchAPYSushi(accountAddress: string) {
const info = await sushiData.sushi.info();
const masterchefInfo = await sushiData.masterchef.info();
const derivedETH = info.derivedETH * Math.pow(10, 18);
this.web3Service.getSlpContract().then((slpContract: Contract) => {
slpContract.methods.totalSupply().call({from: accountAddress}).then((totalSupplyResult: any) => {
const totalSupply = totalSupplyResult;
this.web3Service.getOnsenContract().then((onsenContract: Contract) => {
onsenContract.methods.poolInfo(PID).call({from: accountAddress}).then((rewardPerBlockResult: any) => {
const allocPoint = rewardPerBlockResult['allocPoint'];
slpContract.methods.balanceOf(ONSEN_CONTRACT_ADDRESS_MAIN_NET).call({from: accountAddress}).then((slpBalanceResult: string) => {
const slpBalance = slpBalanceResult;
slpContract.methods.getReserves().call({from: accountAddress}).then((reservesResult: any) => {
const totalValueETH = reservesResult['_reserve1'];
onsenContract.methods.sushiPerBlock().call({from: accountAddress}).then((sushiPerBlockResult: any) => {
const sushiPerBlock = sushiPerBlockResult;
this.apySushi = this.calcSushiAPY(
derivedETH,
sushiPerBlock,
allocPoint,
masterchefInfo.totalAllocPoint,
totalValueETH,
slpBalance,
totalSupply
);
this.apySushi = this.apySushi * 100;
this.apySushi = this.utilsService.parseAmount(this.apySushi.toString(), 16) / 100 ;
this.apySushiChange.next(this.apySushi);
});
});
});
});
});
});
});
}
public getAPYXft(accountAddress: string): void {
if (this.apyXFT) {
this.apyXFTChange.next(this.apyXFT);
} else {
this.fetchAPYXft(accountAddress);
}
}
public fetchAPYXft(accountAddress: string) {
this.web3Service.getStakingContract().then((stakingContract: Contract) => {
stakingContract.methods.rewardRate().call({from: accountAddress}).then((rewardRateResult: any) => {
const rewardRate = rewardRateResult;
stakingContract.methods.totalStaked().call({from: accountAddress}).then((totalStakedResult: any) => {
const totalStaked = totalStakedResult;
this.web3Service.getSlpContract().then((slpContract: Contract) => {
slpContract.methods.totalSupply().call({from: accountAddress}).then((totalSupplyResult: any) => {
const totalSupply = totalSupplyResult;
slpContract.methods.getReserves().call({from: accountAddress}).then((reserveResult: any) => {
const reserve = reserveResult._reserve0;
this.apyXFT = ((rewardRate * XFT_APY_CONST) / ((totalStaked / totalSupply ) * 2 * reserve)) * 100;
this.apyXFT = this.apyXFT.toFixed(2);
this.apyXFTChange.next(this.apyXFT);
});
});
});
});
});
});
}
private calcSushiAPY = (derivedETH, sushiPerBlock, allocPoint, totalAllocPoint, totalValueETH, slpBalance, totalSupply) => {
return (
(derivedETH * BLOCKS_PER_DAY * sushiPerBlock * 3 * 365 * (allocPoint / totalAllocPoint) ) / (totalValueETH * 2 * (slpBalance / totalSupply)));
}
}
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LocalStorageService {
localStorage: Storage;
changes$ = new Subject();
constructor() {
this.localStorage = window.localStorage;
}
get(key: string): any {
if (this.isLocalStorageSupported) {
return JSON.parse(this.localStorage.getItem(key));
}
return null;
}
set(key: string, value: any): boolean {
if (this.isLocalStorageSupported) {
this.localStorage.setItem(key, JSON.stringify(value));
this.changes$.next({
type: 'set',
key,
value
});
return true;
}
return false;
}
remove(key: string): boolean {
if (this.isLocalStorageSupported) {
this.localStorage.removeItem(key);
this.changes$.next({
type: 'remove',
key
});
return true;
}
return false;
}
get isLocalStorageSupported(): boolean {
return !!this.localStorage;
}
}
import {Injectable, OnDestroy} from '@angular/core';
import {AngularWeb3RecentTransactionsService, IAngularWeb3Transaction} from 'angular-web3-components';
import {Subscription} from 'rxjs';
import {Router} from '@angular/router';
import {FormService} from '../../../services/form.service';
import {AuthService} from '../auth/auth.service';
import {StakeSlpService} from './stake-slp.service';
@Injectable({
providedIn: 'root'
})
export class TransactionsService implements OnDestroy {
public transactionsSubscription: Subscription;
private readonly transactionNamesToMethodMapping = [
{
name: 'Approve new allowance for stake SLP',
service: this.stakeSlpService,
methods: ['fetchSLPAllowance'],
routs: ['/stake-now']
},
{
name: 'Stake SLP',
service: this.stakeSlpService,
methods: ['fetchSLPBalance', 'fetchStaked', 'fetchAPYXft', 'fetchAPYSushi'],
routs: ['/stake-now']
},
{
name: 'Withdraw SLP',
service: this.stakeSlpService,
methods: ['fetchSLPBalance', 'fetchStaked', 'fetchAPYXft', 'fetchAPYSushi'],
routs: ['/stake-now']
}
];
constructor(
private recentTransactionsService: AngularWeb3RecentTransactionsService,
private router: Router,
private authService: AuthService,
private formService: FormService,
private stakeSlpService: StakeSlpService
) {
}
public init() {
this.transactionsSubscription = this.recentTransactionsService.transactionStatusChange.subscribe((res: IAngularWeb3Transaction) => {
const transactionMethodData = this.transactionNamesToMethodMapping.find(el => el.name === res.name);
if (transactionMethodData && transactionMethodData.routs.includes(this.router.url)) {
for (const method of transactionMethodData.methods) {
transactionMethodData.service[method](this.authService.user.address);
}
}
});
}
ngOnDestroy() {
this.transactionsSubscription.unsubscribe();
}
}
import {Injectable} from '@angular/core';
import * as BN from 'bn.js';
import {DIVIDER_FOR_BALANCE, WEB3} from '../../../integrations/dictionaries/meta-mask.dictionary';
@Injectable({
providedIn: 'root'
})
export class UtilsService {
public parseAmount(amount: string, decimals: number): number {
const web3 = window[WEB3];
const divider = web3.utils.toBN(10).pow(web3.utils.toBN(decimals));
return new web3.utils.BN(amount).div(divider).toNumber();
}
public convertAmount(amount: any) {
const web3 = window[WEB3];
amount = web3.utils.toWei(amount.toString());
return amount;
}
public parseAmountDivider(amount: string): number {
const web3 = window[WEB3];
const divider = web3.utils.toBN(10).pow(web3.utils.toBN(DIVIDER_FOR_BALANCE));
let result = (new web3.utils.BN(amount)).div(divider).toNumber();
result = result / 100;
return result;
}
public lessOrEqual(amount1: string, amount2: string ) {
const web3 = window[WEB3];
amount1 = web3.utils.toWei(amount1);
return (new web3.utils.BN(amount1)).lte(new web3.utils.BN(amount2));
}
public greaterThen(amount1: string, amount2: string ) {
const web3 = window[WEB3];
amount1 = web3.utils.toWei(amount1);
return (new web3.utils.BN(amount1)).gt(new web3.utils.BN(amount2));
}
public lessThen(amount1: string, amount2: string ) {
const web3 = window[WEB3];
amount1 = web3.utils.toWei(amount1);
return (new web3.utils.BN(amount1)).lt(new web3.utils.BN(amount2));
}
}
export const STAKING_CONTRACT_ADDRESS_MAIN_NET = '0xE5671193A652D2Ba9a36A0EFaDB71cdd86388EC8';
export const ONSEN_CONTRACT_ADDRESS_MAIN_NET = '0xc2EdaD668740f1aA35E4D8f227fB8E17dcA888Cd';
export const SUSHI_TOKEN_ADDRESS_MAIN_NET = '0x6b3595068778dd592e39a122f4f5a5cf09c90fe2';
export const SLP_TOKEN_ADDRESS_MAIN_NET = '0xf39ff863730268c9bb867b3a69d031d1c1614b31';
export const INFURA_PROVIDER_ADDRESS = 'https://ropsten.infura.io/v3/0eb808033ab04d788e5ac66ebe77db6c';
export const LOCAL_STORAGE_ADDRESS_KEY = 'user_address';
export const METAMASK_LINK = 'https://metamask.io/';
export const ETHERSCAN_LINK = 'https://etherscan.io/tx/';
export const ETHEREUM = 'ethereum';
export const WEB3 = 'web3';
export const TOKEN_DECIMALS = 18;
export const DIVIDER_FOR_BALANCE = 16;
export const PID = 149;
export const XFT_APY_CONST = 31536000;
export const BLOCKS_PER_DAY = 6500;
export const METAMASK_ERRORS = {
notInstalled: 'Non-Ethereum browser detected. You should consider trying Mist or MetaMask!',
noAccount: 'There is no account',
};
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {Web3Service} from './services/web3/web3.service';
@NgModule({
imports: [
CommonModule
],
providers: [
Web3Service,
]
})
export class IntegrationsModule { }
import Web3 from 'web3';
import SlpAbi from '../../../../assets/json/contracts/SLP.json';
import StakingAbi from '../../../../assets/json/contracts/STAKING.json';
import OnsenAbi from '../../../../assets/json/contracts/ONSEN.json';
import {
ETHEREUM,
METAMASK_ERRORS,
METAMASK_LINK,
WEB3,
STAKING_CONTRACT_ADDRESS_MAIN_NET, ONSEN_CONTRACT_ADDRESS_MAIN_NET, SLP_TOKEN_ADDRESS_MAIN_NET, INFURA_PROVIDER_ADDRESS
} from '../../dictionaries/meta-mask.dictionary';
import {from, Observable, throwError} from 'rxjs';
import {catchError, map} from 'rxjs/operators';
import {Contract} from 'web3-eth-contract';
export class Web3Service {
private messageResult: string;
constructor() {}
public isMetaMaskInstalled(): boolean {
return !!window[ETHEREUM];
}
public connectToMetaMask(): Observable<string> {
if (!this.isMetaMaskInstalled()) {
window.open(METAMASK_LINK, '_blank');
return throwError(METAMASK_ERRORS.notInstalled);
}
window[WEB3] = new Web3(window[ETHEREUM]);
return from(this.setMetamaskProvider())
.pipe(
map((accounts) => accounts[0]),
catchError(() => {
return throwError(METAMASK_ERRORS.noAccount);
}));
}
public setMetamaskProvider(): Promise<any> {
window[WEB3] = new Web3(window[ETHEREUM]);
return window[ETHEREUM].enable();
}
public setInfuraProvider(): Promise<void> {
return new Promise((resolve) => {
window[WEB3] = new Web3(new Web3.providers.HttpProvider(INFURA_PROVIDER_ADDRESS));
resolve();
});
}
public loadBlockChainData(): Promise<string> {
return new Promise((resolve, reject) => {
const web3 = window[WEB3];
const account = web3.eth.getAccounts();
if (account !== undefined) {
resolve(account);
} else {
this.messageResult = METAMASK_ERRORS.noAccount;
reject(this.messageResult);
}
});
}
public getStakingContract(): Promise<Contract> {
return new Promise((resolve) => {
const web3 = window[WEB3];
const contract = new web3.eth.Contract(StakingAbi, STAKING_CONTRACT_ADDRESS_MAIN_NET);
resolve(contract);
});
}
public getOnsenContract(): Promise<Contract> {
return new Promise((resolve) => {
const web3 = window[WEB3];
const contract = new web3.eth.Contract(OnsenAbi, ONSEN_CONTRACT_ADDRESS_MAIN_NET);
resolve(contract);
});
}
public getSlpContract(): Promise<Contract> {
return new Promise((resolve) => {
const web3 = window[WEB3];
const contract = new web3.eth.Contract(SlpAbi, SLP_TOKEN_ADDRESS_MAIN_NET);
resolve(contract);
});
}
}
<app-header></app-header>
<ng-container *mobxAutorun>
<div class="features-page">
<div class="features-container">
<div class="card-box">
<div [ngSwitch]="selectedCase">
<div class="card-box__switcher">
<div class="card-box__switcher__item" [class.card-box__switcher__item__active]="selectedCase ==='STAKE'" (click)="selectedCase='STAKE'">
Stake
</div>
<div class="card-box__switcher__item" [class.card-box__switcher__item__active]="selectedCase ==='UNSTAKE'" (click)="selectedCase='UNSTAKE'">
Unstake
</div>
<div class="card-box__switcher__item" [class.card-box__switcher__item__active]="selectedCase ==='REWARDS'" (click)="selectedCase='REWARDS'">
Rewards
</div>
</div>
<form class="form-box" [formGroup]="formStake" *ngSwitchCase="'STAKE'">
<div class="form-input-box">
<label for="amount-stake">Amount</label>
<div class="form-input-box-input-text">
<input type="text" id="amount-stake" appNumberOnly formControlName="amount" [class.error]="!stakeAmount.invalid && greaterThen(stakeAmount.value, slpBalance)" >
<span class="text">SLP</span>
</div>
</div>
<p>Balance: {{slpBalanceDivider}} SLP</p>
<p>XFT APY: {{apyXFT}} %</p>
<p>SUSHI ROI: {{apySushi}} %</p>
<button mat-raised-button class="card-box__btn btn" type="button" [class.disabled]="isSubmittingApprove || isSubmittingStake || formStake.invalid || !lessOrEqual(stakeAmount.value, slpBalance)" (click)="submitFormStake()">
<span class="card-box__btn__box" *ngIf="!isSubmittingApprove && !isSubmittingStake" >
<span *ngIf="!stakeAmount.invalid && lessOrEqual(stakeAmount.value, slpAllowance)">Stake</span>
<span *ngIf="!stakeAmount.invalid && greaterThen(stakeAmount.value, slpAllowance) && lessOrEqual(stakeAmount.value, slpBalance)">Approve</span>
<span *ngIf="stakeAmount.invalid || !lessOrEqual(stakeAmount.value, slpBalance)">Approve & Stake</span>
</span>
<span class="card-box__btn__box" *ngIf="isSubmittingApprove" >
<span>Approving</span>
<mat-spinner class="card-box__btn__box__loader" [diameter]="20"></mat-spinner>
</span>
<span class="card-box__btn__box" *ngIf="isSubmittingStake" >
<span>Staking</span>
<mat-spinner class="card-box__btn__box__loader" [diameter]="20"></mat-spinner>
</span>
</button>
</form>
<form class="form-box" [formGroup]="formUnstake" *ngSwitchCase="'UNSTAKE'">
<div class="form-input-box">
<label for="amount-unstake">Amount</label>
<div class="form-input-box-input-text">
<input type="text" id="amount-unstake" appNumberOnly formControlName="amount" #unstakeInput [class.error]="!unstakeAmount.invalid && greaterThen(unstakeInput.value, staked)" >
<span class="text">SLP</span>
</div>
</div>
<p>Staked balance {{stakedDivider}} SLP</p>
<button mat-raised-button class="card-box__btn btn" type="button" [class.disabled]="isSubmittingUnstake || formUnstake.invalid || greaterThen(unstakeInput.value, staked)" (click)="submitFormUnstake()">
<span class="card-box__btn__box" *ngIf="!isSubmittingUnstake" >Unstake</span>
<span class="card-box__btn__box" *ngIf="isSubmittingUnstake" >
<span>Unstaking</span>
<mat-spinner class="card-box__btn__box__loader" [diameter]="20"></mat-spinner>
</span>
</button>
</form>
<div class="form-box" *ngSwitchCase="'REWARDS'">
<p>Available XFT rewards: {{rewardsXFT}}</p>
<p>Available SUSHI rewards: {{rewardsSushi}}</p>
<div class="box-btn">
<!-- <button class="card-box__btn box-btn__left" type="button">{{'FEATURES__PAGE.RESTAKE_BTN' | translate}}</button>-->
<button mat-raised-button class="card-box__btn btn" type="button" [class.disabled]="isSubmittingClaimRewards " (click)="submitClaimRewards()">
<span class="card-box__btn__box" *ngIf="!isSubmittingClaimRewards" >Claim Rewards</span>
<span class="card-box__btn__box" *ngIf="isSubmittingClaimRewards" >
<span>Submitting</span>
<mat-spinner class="card-box__btn__box__loader" [diameter]="20"></mat-spinner>
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</ng-container>
@import "./src/themes/variables";
.features-page{
padding: 0 50px;
.features-container {
border-radius: 18px;
width: 95%;
//min-height: 332px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
max-width: 480px;
min-width: 350px;
background: $card-background;
.card-box{
padding: 38px;
&__switcher{
display: flex;
width: 100%;
justify-content: space-between;
&__item{
font-style: normal;
font-weight: 600;
font-size: 20px;
line-height: 20px;
text-align: center;
text-transform: uppercase;
color: $text-color;
cursor: pointer;
&__active{
padding-bottom: 6px;
border-bottom: solid 4px $form-color;
}
}
}
.box-btn{
display: flex;
justify-content: space-between;
&__left{
margin-right: 4px;
}
&__right{
margin-left: 4px;
}
button{
//max-width: 190px;
}
}
&__btn {
height: 48px;
background: $btn-color;
color: $text-color;
border-radius: 8px;
width: 100%;
border: none;
text-transform: capitalize;
font-size: 20px;
margin-top: 35px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
&.disabled {
opacity: .7;
cursor: unset;
pointer-events: none;
}
&__box{
display: flex;
justify-content: center;
align-items: center;
&__loader {
margin-left: 10px;
}
}
&.gray {
background: $grey-color;
}
}
}
.form-box{
margin-top: 34px;
.form-input-box{
display: grid;
margin-bottom: 10px;
.form-input-box-input-select{
position: relative;
display: flex;
.select{
color: $text-color;
opacity: 0.6;
position: absolute;
right: 14px;
border-left: 1px solid $form-color;
font-size: 18px;
padding-left: 14px;
background: transparent;
border-top: none;
border-right: none;
border-bottom: none;
height: 100%;
}
}
.form-input-box-input-text{
position: relative;
display: flex;
.text{
color: $text-color;
opacity: 0.6;
position: absolute;
right: 14px;
top: 19px;
}
}
label{
margin-bottom: 5px;
text-transform: capitalize;
color:$form-color;
font-style: normal;
font-weight: normal;
font-size: 18px;
line-height: 18px;
}
input{
height: 48px;
border: 1px solid $form-color;
border-radius: 8px;
font-size: 18px;
background: transparent;
color: $text-color;
padding: 0 14px;
width: 100%;
&.error {
border-color: red;
}
}
}
p{
font-weight: 500;
font-size: 16px;
color: $text-color;
margin: 10px 0;
}
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment