Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
parity
fether
Commits
04f762fd
Commit
04f762fd
authored
Jan 16, 2019
by
Luke Schoen
Browse files
merge latest master with Signer and fix conflicts
parents
e122b380
4cdfca0a
Pipeline
#28396
passed with stage
in 1 minute and 49 seconds
Changes
44
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
packages/fether-react/package.json
View file @
04f762fd
...
...
@@ -35,14 +35,18 @@
},
"dependencies"
:
{
"@craco/craco"
:
"^3.2.3"
,
"@parity/api"
:
"^3.0.11"
,
"@parity/contracts"
:
"^3.0.11"
,
"@parity/light.js"
:
"^3.0.11"
,
"@parity/light.js-react"
:
"^3.0.11"
,
"@parity/abi"
:
"^3.0.25"
,
"@parity/api"
:
"^3.0.25"
,
"@parity/contracts"
:
"^3.0.25"
,
"@parity/light.js"
:
"^3.0.25"
,
"@parity/light.js-react"
:
"^3.0.25"
,
"@parity/qr-signer"
:
"^0.3.2"
,
"bignumber.js"
:
"^8.0.1"
,
"bip39"
:
"^2.5.0"
,
"debounce-promise"
:
"^3.1.0"
,
"debug"
:
"^4.1.0"
,
"ethereumjs-tx"
:
"^1.3.7"
,
"ethereumjs-util"
:
"^6.0.0"
,
"ethereumjs-wallet"
:
"^0.6.2"
,
"fether-ui"
:
"^0.2.0"
,
"file-saver"
:
"^2.0.0"
,
...
...
packages/fether-react/public/index.html
View file @
04f762fd
...
...
@@ -4,7 +4,7 @@
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, shrink-to-fit=no"
>
<meta
http-equiv=
"Content-Security-Policy"
content=
"default-src 'self';script-src file: http: 'unsafe-inline'; connect-src file: http: https: ws: wss:;img-src 'self' 'unsafe-inline' file: data: blob: http: https:;style-src 'unsafe-inline' file:;"
>
<meta
http-equiv=
"Content-Security-Policy"
content=
"default-src 'self';script-src file: http:
blob:
'unsafe-inline'; connect-src file: http: https: ws: wss:;img-src 'self' 'unsafe-inline' file: data: blob: http: https:;style-src 'unsafe-inline' file:;"
>
<meta
name=
"theme-color"
content=
"#000000"
>
<!--
manifest.json provides metadata used when your web app is added to the
...
...
packages/fether-react/src/Accounts/AccountsList/AccountsList.js
View file @
04f762fd
...
...
@@ -5,17 +5,19 @@
import
React
,
{
Component
}
from
'
react
'
;
import
{
AccountCard
,
Clickable
,
Header
}
from
'
fether-ui
'
;
import
{
accountsInfo
$
,
withoutLoading
}
from
'
@parity/light.js
'
;
import
{
chainId
$
,
withoutLoading
}
from
'
@parity/light.js
'
;
import
{
inject
,
observer
}
from
'
mobx-react
'
;
import
light
from
'
@parity/light.js-react
'
;
import
Health
from
'
../../Health
'
;
import
Feedback
from
'
./Feedback
'
;
import
withAccountsInfo
from
'
../../utils/withAccountsInfo
'
;
@
withAccountsInfo
@
inject
(
'
createAccountStore
'
,
'
parityStore
'
)
@
light
({
accountsInfo
:
()
=>
accountsInfo
$
().
pipe
(
withoutLoading
())
chainId
:
()
=>
chainId
$
().
pipe
(
withoutLoading
())
})
@
inject
(
'
createAccountStore
'
,
'
parityStore
'
)
@
observer
class
AccountsList
extends
Component
{
handleClick
=
({
...
...
@@ -34,9 +36,13 @@ class AccountsList extends Component {
};
render
()
{
const
{
accountsInfo
}
=
this
.
props
;
const
{
accountsInfo
,
chainId
}
=
this
.
props
;
const
accountsList
=
Object
.
keys
(
accountsInfo
);
const
accountsList
=
Object
.
keys
(
accountsInfo
).
filter
(
key
=>
!
accountsInfo
[
key
].
chainId
||
accountsInfo
[
key
].
chainId
===
parseInt
(
chainId
,
10
)
);
const
accountsListLength
=
accountsList
&&
accountsList
.
length
;
return
(
...
...
@@ -64,13 +70,8 @@ class AccountsList extends Component {
<
AccountCard
address
=
{
address
}
className
=
'
-clickable
'
name
=
{
accountsInfo
&&
accountsInfo
[
address
]
&&
(
accountsInfo
[
address
].
name
?
accountsInfo
[
address
].
name
:
'
(No name)
'
)
}
type
=
{
accountsInfo
[
address
].
type
}
name
=
{
accountsInfo
[
address
].
name
||
'
(no name)
'
}
shortAddress
/>
<
/li
>
...
...
packages/fether-react/src/Accounts/CreateAccount/AccountImportOptions/AccountImportOptions.js
View file @
04f762fd
...
...
@@ -5,20 +5,20 @@
import
React
,
{
Component
}
from
'
react
'
;
import
{
addressShort
,
Card
,
Form
as
FetherForm
}
from
'
fether-ui
'
;
import
{
accounts$
,
withoutLoading
}
from
'
@parity/light.js
'
;
import
light
from
'
@parity/light.js-react
'
;
import
{
inject
,
observer
}
from
'
mobx-react
'
;
@
light
({
accounts
:
()
=>
accounts$
().
pipe
(
withoutLoading
())
})
import
Scanner
from
'
../../../Scanner
'
;
import
withAccountsInfo
from
'
../../../utils/withAccountsInfo
'
;
@
withAccountsInfo
@
inject
(
'
createAccountStore
'
)
@
observer
class
AccountImportOptions
extends
Component
{
state
=
{
error
:
''
,
isLoading
:
false
,
phrase
:
''
phrase
:
''
,
importingFromSigner
:
false
};
handleNextStep
=
async
()
=>
{
...
...
@@ -87,11 +87,42 @@ class AccountImportOptions extends Component {
}
};
hasExistingAddressForImport
=
addressForImport
=>
{
const
{
accounts
}
=
this
.
props
;
const
isExistingAddress
=
accounts
.
map
(
address
=>
address
&&
address
.
toLowerCase
())
.
includes
(
addressForImport
.
toLowerCase
());
handleSignerImported
=
async
({
address
,
chainId
:
chainIdString
})
=>
{
const
{
createAccountStore
:
{
importFromSigner
}
}
=
this
.
props
;
if
(
!
address
||
!
chainIdString
)
{
this
.
setState
({
error
:
'
Invalid QR code.
'
});
return
;
}
const
chainId
=
parseInt
(
chainIdString
);
if
(
this
.
hasExistingAddressForImport
(
address
,
chainId
))
{
return
;
}
await
importFromSigner
({
address
,
chainId
});
this
.
handleNextStep
();
};
handleSignerImport
=
()
=>
{
this
.
setState
({
importingFromSigner
:
true
});
};
hasExistingAddressForImport
=
(
addressForImport
,
chainId
)
=>
{
const
{
accountsInfo
}
=
this
.
props
;
const
isExistingAddress
=
Object
.
keys
(
accountsInfo
).
some
(
key
=>
key
.
toLowerCase
()
===
addressForImport
.
toLowerCase
()
&&
(
!
accountsInfo
[
key
].
chainId
||
!
chainId
||
accountsInfo
[
key
].
chainId
===
chainId
)
);
if
(
isExistingAddress
)
{
this
.
setState
({
...
...
@@ -108,46 +139,76 @@ class AccountImportOptions extends Component {
history
,
location
:
{
pathname
}
}
=
this
.
props
;
const
{
error
,
phrase
}
=
this
.
state
;
const
{
error
,
importingFromSigner
,
phrase
}
=
this
.
state
;
const
currentStep
=
pathname
.
slice
(
-
1
);
const
jsonCard
=
(
<
div
key
=
'
createAccount
'
>
<
div
className
=
'
text -centered
'
>
<
p
>
Recover
from
JSON
Keyfile
<
/p
>
<
FetherForm
.
InputFile
label
=
'
JSON Backup Keyfile
'
onChangeFile
=
{
this
.
handleChangeFile
}
required
/>
<
Card
>
<
div
key
=
'
createAccount
'
>
<
div
className
=
'
text -centered
'
>
<
p
>
Recover
from
JSON
Keyfile
<
/p
>
<
FetherForm
.
InputFile
label
=
'
JSON Backup Keyfile
'
onChangeFile
=
{
this
.
handleChangeFile
}
required
/>
<
/div
>
<
/div
>
<
/div
>
<
/Card
>
);
const
signerCard
=
(
<
Card
>
<
div
key
=
'
createAccount
'
>
<
div
className
=
'
text -centered
'
>
<
p
>
Recover
from
Parity
Signer
<
/p
>
{
importingFromSigner
?
(
<
Scanner
onScan
=
{
this
.
handleSignerImported
}
label
=
'
Please show the QR code of the account on the webcam.
'
/>
)
:
(
<
button
className
=
'
button -footer
'
onClick
=
{
this
.
handleSignerImport
}
>
Scan
QR
code
<
/button
>
)}
<
/div
>
<
/div
>
<
/Card
>
);
const
phraseCard
=
(
<
div
key
=
'
importBackup
'
>
<
div
className
=
'
text -centered
'
>
<
p
>
Recover
from
Seed
Phrase
<
/p
>
<
FetherForm
.
Field
as
=
'
textarea
'
label
=
'
Recovery phrase
'
onChange
=
{
this
.
handlePhraseChange
}
required
phrase
=
{
phrase
}
/
>
{
this
.
renderButton
()}
<
Card
>
<
div
key
=
'
importBackup
'
>
<
div
className
=
'
text -centered
'
>
<
p
>
Recover
from
Seed
Phrase
<
/p
>
<
FetherForm
.
Field
as
=
'
textarea
'
label
=
'
Recovery phrase
'
onChange
=
{
this
.
handlePhraseChange
}
required
phrase
=
{
phrase
}
/
>
{
this
.
renderButton
()}
<
/div
>
<
/div
>
<
/
div
>
<
/
Card
>
);
return
(
<
div
className
=
'
center-md
'
>
<
Card
>
{
jsonCard
}
<
/Card
>
{
!
importingFromSigner
&&
jsonCard
}
<
br
/>
{
signerCard
}
<
br
/>
<
Card
>
{
phraseCard
}
<
/Card
>
{
!
importingFromSigner
&&
phraseCard
}
<
br
/>
<
p
>
{
error
}
<
/p
>
<
nav
className
=
'
form-nav -space-around
'
>
...
...
packages/fether-react/src/Accounts/CreateAccount/AccountName/AccountName.js
View file @
04f762fd
...
...
@@ -26,13 +26,32 @@ class AccountName extends Component {
handleSubmit
=
()
=>
{
const
{
createAccountStore
,
history
,
location
:
{
pathname
}
}
=
this
.
props
;
const
currentStep
=
pathname
.
slice
(
-
1
);
history
.
push
(
`/accounts/new/
${
+
currentStep
+
1
}
`
);
if
(
createAccountStore
.
noPrivateKey
)
{
// Save Signer account to Parity without asking for a password
createAccountStore
.
saveAccountToParity
()
.
then
(
res
=>
{
createAccountStore
.
clear
();
history
.
push
(
'
/accounts
'
);
})
.
catch
(
err
=>
{
console
.
error
(
err
);
this
.
setState
({
error
:
err
.
text
});
});
}
else
{
// Ask for a password otherwise
history
.
push
(
`/accounts/new/
${
+
currentStep
+
1
}
`
);
}
};
render
()
{
...
...
@@ -45,12 +64,13 @@ class AccountName extends Component {
renderCardWhenImported
=
()
=>
{
const
{
createAccountStore
:
{
address
,
name
}
createAccountStore
:
{
address
,
name
,
noPrivateKey
}
}
=
this
.
props
;
return
(
<
AccountCard
address
=
{
address
}
type
=
{
noPrivateKey
?
'
signer
'
:
'
node
'
}
drawers
=
{[
this
.
renderDrawer
()]}
name
=
{
name
||
'
(no name)
'
}
/
>
...
...
@@ -89,6 +109,7 @@ class AccountName extends Component {
renderDrawer
=
()
=>
{
const
{
createAccountStore
:
{
address
,
name
},
error
,
history
,
location
:
{
pathname
}
}
=
this
.
props
;
...
...
@@ -107,6 +128,7 @@ class AccountName extends Component {
type
=
'
text
'
value
=
{
name
}
/
>
{
error
&&
<
p
>
{
error
}
<
/p>
}
<
nav
className
=
'
form-nav -space-around
'
>
{
currentStep
>
1
&&
(
<
button
...
...
packages/fether-react/src/Accounts/CreateAccount/AccountPassword/AccountPassword.js
View file @
04f762fd
...
...
@@ -29,7 +29,7 @@ class AccountPassword extends Component {
const
{
createAccountStore
,
history
}
=
this
.
props
;
const
{
confirm
,
password
}
=
this
.
state
;
event
&&
event
.
preventDefault
();
event
.
preventDefault
();
if
(
!
createAccountStore
.
jsonString
&&
confirm
!==
password
)
{
this
.
setState
({
...
...
packages/fether-react/src/Accounts/CreateAccount/CreateAccount.js
View file @
04f762fd
...
...
@@ -4,10 +4,8 @@
// SPDX-License-Identifier: BSD-3-Clause
import
React
,
{
Component
}
from
'
react
'
;
import
{
accountsInfo$
}
from
'
@parity/light.js
'
;
import
{
Header
}
from
'
fether-ui
'
;
import
{
inject
,
observer
}
from
'
mobx-react
'
;
import
light
from
'
@parity/light.js-react
'
;
import
{
Link
,
Route
}
from
'
react-router-dom
'
;
import
AccountCopyPhrase
from
'
./AccountCopyPhrase
'
;
...
...
@@ -16,9 +14,10 @@ import AccountRewritePhrase from './AccountRewritePhrase';
import
AccountName
from
'
./AccountName
'
;
import
AccountPassword
from
'
./AccountPassword
'
;
import
Health
from
'
../../Health
'
;
import
withAccountsInfo
from
'
../../utils/withAccountsInfo
'
;
@
light
({
accountsInfo
:
accountsInfo$
})
@
inject
(
'
createAccountStore
'
)
@
withAccountsInfo
@
observer
class
CreateAccount
extends
Component
{
constructor
(
props
)
{
...
...
@@ -45,7 +44,6 @@ class CreateAccount extends Component {
}
=
this
.
props
;
createAccountStore
.
clear
();
createAccountStore
.
setIsImport
(
!
createAccountStore
.
isImport
);
// If we were further in the account creation, go back to step 1
if
(
step
>
1
)
{
history
.
push
(
'
/accounts/new/1
'
);
...
...
packages/fether-react/src/BackupAccount/BackupAccount.js
View file @
04f762fd
...
...
@@ -6,8 +6,6 @@
import
React
,
{
Component
}
from
'
react
'
;
import
{
AccountHeader
,
Card
,
Form
as
FetherForm
}
from
'
fether-ui
'
;
import
{
observer
}
from
'
mobx-react
'
;
import
{
accountsInfo$
}
from
'
@parity/light.js
'
;
import
light
from
'
@parity/light.js-react
'
;
import
{
Link
,
withRouter
}
from
'
react-router-dom
'
;
import
backupAccount
from
'
../utils/backupAccount
'
;
...
...
@@ -15,9 +13,6 @@ import withAccount from '../utils/withAccount';
@
withRouter
@
withAccount
@
light
({
accountsInfo
:
accountsInfo$
})
@
observer
class
BackupAccount
extends
Component
{
state
=
{
...
...
@@ -31,14 +26,17 @@ class BackupAccount extends Component {
};
handleSubmit
=
event
=>
{
const
{
accountAddress
,
history
}
=
this
.
props
;
const
{
account
:
{
address
},
history
}
=
this
.
props
;
const
{
password
}
=
this
.
state
;
event
&&
event
.
preventDefault
();
this
.
setState
({
isLoading
:
true
});
backupAccount
(
a
ccountA
ddress
,
password
)
backupAccount
(
address
,
password
)
.
then
(
res
=>
{
/*
FIXME: this timeout is a placeholder for after the backup file is saved.
...
...
@@ -58,23 +56,18 @@ class BackupAccount extends Component {
render
()
{
const
{
accountsInfo
,
history
,
location
:
{
pathname
}
account
:
{
name
,
address
,
type
},
history
}
=
this
.
props
;
const
{
isLoading
,
message
,
password
}
=
this
.
state
;
const
accountAddress
=
pathname
.
slice
(
-
42
);
return
(
<
div
>
<
AccountHeader
address
=
{
a
ccountA
ddress
}
address
=
{
address
}
copyAddress
name
=
{
accountsInfo
&&
accountsInfo
[
accountAddress
]
&&
accountsInfo
[
accountAddress
].
name
}
name
=
{
name
}
type
=
{
type
}
left
=
{
<
Link
to
=
'
/accounts
'
className
=
'
icon -back
'
>
Back
...
...
packages/fether-react/src/Scanner/Scanner.js
0 → 100644
View file @
04f762fd
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
// SPDX-License-Identifier: BSD-3-Clause
import
React
from
'
react
'
;
import
QrSigner
from
'
@parity/qr-signer
'
;
import
loading
from
'
../assets/img/icons/loading.svg
'
;
export
default
class
Scanner
extends
React
.
PureComponent
{
state
=
{
webcamError
:
null
,
isLoading
:
true
};
componentDidMount
()
{
this
.
checkForWebcam
();
if
(
navigator
.
mediaDevices
)
{
navigator
.
mediaDevices
.
addEventListener
(
'
devicechange
'
,
this
.
checkForWebcam
);
}
}
componentWillUnmount
()
{
if
(
navigator
.
mediaDevices
&&
navigator
.
mediaDevices
.
ondevicechange
)
{
navigator
.
mediaDevices
.
removeEventListener
(
'
devicechange
'
,
this
.
checkForWebcam
);
}
}
checkForWebcam
=
async
()
=>
{
if
(
navigator
.
mediaDevices
&&
navigator
.
mediaDevices
.
getUserMedia
)
{
try
{
await
navigator
.
mediaDevices
.
getUserMedia
({
video
:
true
});
this
.
setState
({
webcamError
:
null
,
isLoading
:
false
});
}
catch
(
e
)
{
let
errorMessage
;
switch
(
e
.
name
)
{
case
'
NotAllowedError
'
:
case
'
SecurityError
'
:
errorMessage
=
'
Access to the webcam was refused.
'
;
break
;
case
'
NotFoundError
'
:
case
'
OverconstrainedError
'
:
errorMessage
=
'
No webcam found on the device.
'
;
break
;
case
'
NotReadableError
'
:
errorMessage
=
'
Webcam hardware error. Try restarting your computer
'
;
break
;
default
:
errorMessage
=
'
Unknown error.
'
;
}
this
.
setState
({
webcamError
:
errorMessage
,
isLoading
:
false
});
}
}
};
render
()
{
const
{
onScan
,
label
}
=
this
.
props
;
const
{
webcamError
,
isLoading
}
=
this
.
state
;
const
size
=
300
;
return
(
<
div
>
{
isLoading
?
(
<
img
alt
=
'
loading
'
src
=
{
loading
}
/
>
)
:
webcamError
?
(
<
p
>
{
webcamError
}
<
/p
>
)
:
(
<
div
>
<
p
>
{
label
}
<
/p
>
<
br
/>
<
QrSigner
scan
onScan
=
{
onScan
}
size
=
{
size
}
/
>
<
/div
>
)}
<
/div
>
);
}
}
packages/fether-react/src/Scanner/index.js
0 → 100644
View file @
04f762fd
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
// SPDX-License-Identifier: BSD-3-Clause
import
Scanner
from
'
./Scanner
'
;
export
default
Scanner
;
packages/fether-react/src/Send/ScanSignedTx/ScanSignedTx.js
0 → 100644
View file @
04f762fd
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
// SPDX-License-Identifier: BSD-3-Clause
import
React
,
{
Component
}
from
'
react
'
;
import
{
Card
,
Header
}
from
'
fether-ui
'
;
import
{
inject
,
observer
}
from
'
mobx-react
'
;
import
{
Link
,
Redirect
}
from
'
react-router-dom
'
;
import
Scanner
from
'
../../Scanner
'
;
import
{
withProps
}
from
'
recompose
'
;
import
RequireHealth
from
'
../../RequireHealthOverlay
'
;
import
withAccount
from
'
../../utils/withAccount.js
'
;
import
withTokens
from
'
../../utils/withTokens
'
;
@
inject
(
'
sendStore
'
)
@
withAccount
@
withTokens
@
withProps
(({
match
:
{
params
:
{
tokenAddress
}
},
tokens
})
=>
({
token
:
tokens
[
tokenAddress
]
}))
@
observer
class
ScanSignedTx
extends
Component
{
state
=
{
error
:
null
};
onScanSignedTx
=
signature
=>
{
const
{
account
:
{
address
:
accountAddress
},
history
,
sendStore
:
{
signRaw
},
token
}
=
this
.
props
;