Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
offshiftXFT
protocol-main
Commits
ce39a68a
Commit
ce39a68a
authored
3 years ago
by
XFT-dev
Browse files
Options
Download
Email Patches
Plain Diff
initializing repo
parent
8605a110
Changes
144
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1141 additions
and
0 deletions
+1141
-0
frontend/src/app/app.component.html
frontend/src/app/app.component.html
+2
-0
frontend/src/app/app.component.scss
frontend/src/app/app.component.scss
+0
-0
frontend/src/app/app.component.spec.ts
frontend/src/app/app.component.spec.ts
+27
-0
frontend/src/app/app.component.ts
frontend/src/app/app.component.ts
+15
-0
frontend/src/app/app.module.ts
frontend/src/app/app.module.ts
+63
-0
frontend/src/app/app.service.ts
frontend/src/app/app.service.ts
+23
-0
frontend/src/app/components/header/header.component.html
frontend/src/app/components/header/header.component.html
+29
-0
frontend/src/app/components/header/header.component.scss
frontend/src/app/components/header/header.component.scss
+56
-0
frontend/src/app/components/header/header.component.ts
frontend/src/app/components/header/header.component.ts
+26
-0
frontend/src/app/core/guards/auth/auth.guard.ts
frontend/src/app/core/guards/auth/auth.guard.ts
+20
-0
frontend/src/app/core/services/auth/auth.service.ts
frontend/src/app/core/services/auth/auth.service.ts
+68
-0
frontend/src/app/core/services/services/stake-slp.service.ts
frontend/src/app/core/services/services/stake-slp.service.ts
+275
-0
frontend/src/app/core/services/services/storage.service.ts
frontend/src/app/core/services/services/storage.service.ts
+44
-0
frontend/src/app/core/services/services/transactions.service.ts
...nd/src/app/core/services/services/transactions.service.ts
+58
-0
frontend/src/app/core/services/services/utils.service.ts
frontend/src/app/core/services/services/utils.service.ts
+48
-0
frontend/src/app/integrations/dictionaries/meta-mask.dictionary.ts
...src/app/integrations/dictionaries/meta-mask.dictionary.ts
+21
-0
frontend/src/app/integrations/integrations.module.ts
frontend/src/app/integrations/integrations.module.ts
+13
-0
frontend/src/app/integrations/services/web3/web3.service.ts
frontend/src/app/integrations/services/web3/web3.service.ts
+88
-0
frontend/src/app/pages/form/form.component.html
frontend/src/app/pages/form/form.component.html
+97
-0
frontend/src/app/pages/form/form.component.scss
frontend/src/app/pages/form/form.component.scss
+168
-0
No files found.
frontend/src/app/app.component.html
0 → 100644
View file @
ce39a68a
<router-outlet></router-outlet>
This diff is collapsed.
Click to expand it.
frontend/src/app/app.component.scss
0 → 100644
View file @
ce39a68a
This diff is collapsed.
Click to expand it.
frontend/src/app/app.component.spec.ts
0 → 100644
View file @
ce39a68a
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!
'
);
}));
});
This diff is collapsed.
Click to expand it.
frontend/src/app/app.component.ts
0 → 100644
View file @
ce39a68a
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
();
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/app.module.ts
0 → 100644
View file @
ce39a68a
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
{
}
This diff is collapsed.
Click to expand it.
frontend/src/app/app.service.ts
0 → 100644
View file @
ce39a68a
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
();
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/components/header/header.component.html
0 → 100644
View file @
ce39a68a
<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>
This diff is collapsed.
Click to expand it.
frontend/src/app/components/header/header.component.scss
0 → 100644
View file @
ce39a68a
@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;
// }
// }
//}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/components/header/header.component.ts
0 → 100644
View file @
ce39a68a
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
;
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/core/guards/auth/auth.guard.ts
0 → 100644
View file @
ce39a68a
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
;
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/core/services/auth/auth.service.ts
0 → 100644
View file @
ce39a68a
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
;
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/core/services/services/stake-slp.service.ts
0 → 100644
View file @
ce39a68a
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
)));
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/core/services/services/storage.service.ts
0 → 100644
View file @
ce39a68a
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
;
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/core/services/services/transactions.service.ts
0 → 100644
View file @
ce39a68a
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
();
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/core/services/services/utils.service.ts
0 → 100644
View file @
ce39a68a
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
));
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/integrations/dictionaries/meta-mask.dictionary.ts
0 → 100644
View file @
ce39a68a
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
'
,
};
This diff is collapsed.
Click to expand it.
frontend/src/app/integrations/integrations.module.ts
0 → 100644
View file @
ce39a68a
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
{
}
This diff is collapsed.
Click to expand it.
frontend/src/app/integrations/services/web3/web3.service.ts
0 → 100644
View file @
ce39a68a
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
);
});
}
}
This diff is collapsed.
Click to expand it.
frontend/src/app/pages/form/form.component.html
0 → 100644
View file @
ce39a68a
<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>
This diff is collapsed.
Click to expand it.
frontend/src/app/pages/form/form.component.scss
0 → 100644
View file @
ce39a68a
@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
;
}
}
}
}
This diff is collapsed.
Click to expand it.
Prev
1
2
3
4
5
6
7
8
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment