sign pinless
This commit is contained in:
parent
a359f3bc9d
commit
bdeb6d2bbe
|
@ -11,6 +11,9 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script>
|
||||
window.statusWeb3 = window.web3;
|
||||
</script>
|
||||
<script src="/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import EmbarkJS from 'Embark/EmbarkJS';
|
||||
import TapWalletFactory from 'Embark/contracts/TapWalletFactory';
|
||||
import TapWallet from 'Embark/contracts/TapWallet';
|
||||
import { emptyAddress } from '../utils';
|
||||
|
||||
export const NEW_WALLET = 'NEW_WALLET';
|
||||
export const newWallet = () => ({
|
||||
|
@ -64,14 +65,16 @@ export const loadingWallet = (index) => ({
|
|||
});
|
||||
|
||||
export const WALLET_LOADED = 'WALLET_LOADED';
|
||||
export const walletLoaded = (index, address, name, keycard, value, icon) => ({
|
||||
export const walletLoaded = (index, address, nonce, name, keycard, balance, icon, maxTxValue) => ({
|
||||
type: WALLET_LOADED,
|
||||
index,
|
||||
address,
|
||||
nonce,
|
||||
name,
|
||||
keycard,
|
||||
value,
|
||||
icon
|
||||
balance,
|
||||
icon,
|
||||
maxTxValue,
|
||||
});
|
||||
|
||||
export const NETWORK_ID_LOADED = "NETWORK_ID_LOADED";
|
||||
|
@ -93,6 +96,15 @@ export const loadNetworkID = () => {
|
|||
export const enableEthereum = () => {
|
||||
if (window.ethereum) {
|
||||
window.web3 = new Web3(ethereum);
|
||||
//FIXME: hack
|
||||
try {
|
||||
// alert(statusWeb3)
|
||||
web3.eth.personal.signMessagePinless = statusWeb3.personal.signMessagePinless;
|
||||
// alert(web3.eth.personal.signMessagePinless)
|
||||
} catch(err){
|
||||
alert(err)
|
||||
}
|
||||
|
||||
return (dispatch) => {
|
||||
ethereum.enable()
|
||||
.then(() => {
|
||||
|
@ -125,6 +137,7 @@ export const loadOwner = () => {
|
|||
return web3.eth.getAccounts()
|
||||
.then((accounts) => {
|
||||
const owner = accounts[0];
|
||||
// web3.eth.personal.signMessagePinless("hello", owner)
|
||||
dispatch(ownerLoaded(owner))
|
||||
dispatch(loadWallets(owner))
|
||||
dispatch(loadOwnerBalance(owner))
|
||||
|
@ -186,15 +199,17 @@ export const loadWallet = (owner, index) => {
|
|||
walletContract.address = address;
|
||||
|
||||
const name = await walletContract.methods.name().call();
|
||||
const value = await web3.eth.getBalance(address);
|
||||
const balance = await web3.eth.getBalance(address);
|
||||
const keycard = await walletContract.methods.keycard().call();
|
||||
const nonce = await walletContract.methods.nonce().call();
|
||||
const maxTxValue = await walletContract.methods.settings().call();
|
||||
|
||||
let icon = "";
|
||||
try {
|
||||
icon = String.fromCodePoint(name);
|
||||
} catch(e){}
|
||||
|
||||
dispatch(walletLoaded(index, address, name, keycard, value, icon))
|
||||
dispatch(walletLoaded(index, address, nonce, name, keycard, balance, icon, maxTxValue))
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -207,7 +222,7 @@ export const newWalletSelectIcon = (icon) => {
|
|||
}
|
||||
|
||||
export const NEW_WALLET_CANCEL = "NEW_WALLET_CANCEL";
|
||||
export const newWalletCancel = (icon) => {
|
||||
export const newWalletCancel = () => {
|
||||
return {
|
||||
type: NEW_WALLET_CANCEL
|
||||
}
|
||||
|
@ -231,21 +246,36 @@ export const walletCreationError = (error) => ({
|
|||
error
|
||||
});
|
||||
|
||||
export const NEW_WALLET_FORM_KEYCARD_ADDRESS_CHANGED = "NEW_WALLET_FORM_KEYCARD_ADDRESS_CHANGED";
|
||||
export const newWalletFormKeycardAddressChanged = (address) => ({
|
||||
type: NEW_WALLET_FORM_KEYCARD_ADDRESS_CHANGED,
|
||||
address
|
||||
});
|
||||
|
||||
export const NEW_WALLET_FORM_MAX_TX_VALUE_CHANGED = "NEW_WALLET_FORM_MAX_TX_VALUE_CHANGED";
|
||||
export const newWalletFormMaxTxValueChanged = (value) => ({
|
||||
type: NEW_WALLET_FORM_MAX_TX_VALUE_CHANGED,
|
||||
value
|
||||
});
|
||||
|
||||
export const createWallet = () => {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const icon = state.newWalletForm.icon;
|
||||
const maxTxValue = web3.utils.toWei(state.newWalletForm.maxTxValue);
|
||||
const keycardAddress = state.newWalletForm.keycardAddress || emptyAddress;
|
||||
const codePoint = icon.codePointAt(0);
|
||||
const name = "0x" + codePoint.toString(16);
|
||||
const create = TapWalletFactory.methods.create(name);
|
||||
const create = TapWalletFactory.methods.create(name, keycardAddress, maxTxValue);
|
||||
const walletIndex = state.wallets.length;
|
||||
|
||||
try {
|
||||
const estimatedGas = await create.estimateGas()
|
||||
create.send({ from: state.owner, gas: estimatedGas,})
|
||||
create.send({ from: state.owner, gas: estimatedGas })
|
||||
.then((receipt) => {
|
||||
console.log(receipt)
|
||||
dispatch(walletCreated(receipt))
|
||||
dispatch(newWalletCancel())
|
||||
dispatch(loadWallets(state.owner))
|
||||
})
|
||||
.catch((err) => {
|
||||
|
@ -259,3 +289,85 @@ export const createWallet = () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SELECT_WALLET = 'SELECT_WALLET';
|
||||
export const selectWallet = (index) => ({
|
||||
type: SELECT_WALLET,
|
||||
index
|
||||
});
|
||||
|
||||
export const CLOSE_SELECTED_WALLET = 'CLOSE_SELECTED_WALLET';
|
||||
export const closeSelectedWallet = (index) => ({
|
||||
type: CLOSE_SELECTED_WALLET,
|
||||
});
|
||||
|
||||
export const TOPPING_UP_WALLET = 'TOPPING_UP_WALLET';
|
||||
export const toppingUpWallet = (index) => ({
|
||||
type: TOPPING_UP_WALLET,
|
||||
index
|
||||
});
|
||||
|
||||
export const ERROR_TOPPING_UP_WALLET = 'ERROR_TOPPING_UP_WALLET';
|
||||
export const errorToppingUpWallet = (index) => ({
|
||||
type: ERROR_TOPPING_UP_WALLET,
|
||||
index
|
||||
});
|
||||
|
||||
export const WALLET_TOPPED_UP = 'WALLET_TOPPED_UP';
|
||||
export const walletToppedUp = (index) => ({
|
||||
type: WALLET_TOPPED_UP,
|
||||
index
|
||||
});
|
||||
|
||||
export const topUpWallet = (index, address, value) => {
|
||||
return async (dispatch, getState) => {
|
||||
const owner = getState().owner;
|
||||
const tx = {
|
||||
from: owner,
|
||||
to: address,
|
||||
value: web3.utils.toWei("0.001"),
|
||||
}
|
||||
|
||||
const gas = await web3.eth.estimateGas(tx);
|
||||
tx.gas = gas;
|
||||
|
||||
dispatch(toppingUpWallet(index));
|
||||
web3.eth.sendTransaction(tx)
|
||||
.then(() => {
|
||||
dispatch(loadWallet(owner, index))
|
||||
dispatch(walletToppedUp(index))
|
||||
})
|
||||
.catch((err) => {
|
||||
dispatch(errorToppingUpWallet(index))
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
export const KEYCARD_DISCOVERED = "KEYCARD_DISCOVERED";
|
||||
export const keycardDiscovered = (sig) => {
|
||||
//FIXME: put a random message
|
||||
const address = web3.eth.accounts.recover("0x112233", sig)
|
||||
return {
|
||||
type: KEYCARD_DISCOVERED,
|
||||
address,
|
||||
}
|
||||
}
|
||||
|
||||
export const signMessagePinless = (message) => {
|
||||
return (dispatch, getState) => {
|
||||
const owner = getState().owner;
|
||||
// web3 0.2 style using status injected web3
|
||||
try {
|
||||
//FIXME: put a random message
|
||||
web3.eth.personal.signMessagePinless("112233", "0x0000000000000000000000000000000000000000", "", function(err, sig) {
|
||||
if (err) {
|
||||
dispatch(web3Error(err))
|
||||
} else {
|
||||
dispatch(keycardDiscovered(sig));
|
||||
}
|
||||
})
|
||||
} catch(err) {
|
||||
dispatch(web3Error(err))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import EmbarkJS from 'Embark/EmbarkJS';
|
|||
|
||||
import WalletsList from '../containers/WalletsList';
|
||||
import NewWalletDialog from '../containers/NewWalletDialog';
|
||||
import SelectedWalletDialog from '../containers/SelectedWalletDialog';
|
||||
import TapWalletFactory from 'Embark/contracts/TapWalletFactory';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
|
@ -58,6 +59,7 @@ const App = (props) => {
|
|||
|
||||
<div>
|
||||
<NewWalletDialog />
|
||||
<SelectedWalletDialog />
|
||||
{body}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@ import DialogContentText from '@material-ui/core/DialogContentText';
|
|||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||
|
||||
const icons = ["💳", "👛", "💸", "😺"]
|
||||
|
||||
|
@ -32,7 +35,7 @@ function Transition(props) {
|
|||
return <Slide direction="up" {...props} />;
|
||||
}
|
||||
|
||||
const NewWalletDialog = ({open, creating, selected, onIconClick, onCancelClick, onCreateClick, error}) => (
|
||||
const NewWalletDialog = ({open, creating, selected, keycard, onIconClick, onCancelClick, onCreateClick, onKeycardChange, error, onTapButtonClick, onMaxTxValueChange, maxTxValue}) => (
|
||||
<Dialog
|
||||
fullScreen
|
||||
TransitionComponent={Transition}
|
||||
|
@ -40,21 +43,57 @@ const NewWalletDialog = ({open, creating, selected, onIconClick, onCancelClick,
|
|||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">Choose an icon to identify your new Wallet</DialogTitle>
|
||||
<DialogTitle id="alert-dialog-title">New Wallet</DialogTitle>
|
||||
<DialogContent>
|
||||
<div>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
Choose an icon to identify your wallet
|
||||
</Typography>
|
||||
<div style={{marginBottom: 16}}>
|
||||
{icons.map((icon) =>
|
||||
<a key={icon} onClick={() => onIconClick(icon) }
|
||||
style={selected == icon ? selectedIconStyles : iconStyles}>{icon}</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Maximum transaction value"
|
||||
type="text"
|
||||
value={maxTxValue}
|
||||
style={{marginBottom: 16}}
|
||||
fullWidth
|
||||
onChange={(event) => onMaxTxValueChange(event.currentTarget.value)}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start" style={{paddingBottom: 7}}>Ξ</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Keycard address"
|
||||
type="text"
|
||||
value={keycard}
|
||||
style={{marginBottom: 16}}
|
||||
fullWidth
|
||||
onChange={(event) => onKeycardChange(event.currentTarget.value)}
|
||||
/>
|
||||
|
||||
<div style={{margin: 16, textAlign: "center"}}>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
or
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<Button onClick={() => onTapButtonClick("hello world")} size="large" color="primary" variant="contained" fullWidth>
|
||||
CONNECT TAPPING YOUR KEYCARD
|
||||
</Button>
|
||||
|
||||
{creating && <div style={{marginTop: 50, textAlign: "center"}}>
|
||||
<CircularProgress />
|
||||
</div>}
|
||||
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
{!creating &&
|
||||
<Button onClick={onCancelClick} color="primary">
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
import React from 'react';
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
import Zoom from '@material-ui/core/Zoom';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
|
||||
const formattedBalance = (balance) => {
|
||||
if (balance) {
|
||||
return web3.utils.fromWei(balance);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
const styles = {
|
||||
appBar: {
|
||||
position: 'relative',
|
||||
marginBottom: 32,
|
||||
}
|
||||
};
|
||||
|
||||
function Transition(props) {
|
||||
// return <Slide direction="up" {...props} />;
|
||||
return <Zoom {...props} />;
|
||||
}
|
||||
|
||||
const SelectedWalletDialog = ({open, onCloseClick, icon, address, nonce, keycardAddress, balance, index, onTopUp, toppingUp, maxTxValue}) => (
|
||||
<Dialog
|
||||
fullScreen
|
||||
TransitionComponent={Transition}
|
||||
open={open}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<AppBar style={styles.appBar}>
|
||||
<Toolbar>
|
||||
<IconButton color="inherit" onClick={onCloseClick} aria-label="Close">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" color="inherit">
|
||||
Wallet {icon}
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
<DialogContent>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
🔖 Wallet Address
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{address}
|
||||
</Typography>
|
||||
|
||||
<Divider style={{margin: "16px 0"}} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
💳 Keycard Address
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{keycardAddress || "0x"}
|
||||
</Typography>
|
||||
|
||||
<Divider style={{margin: "16px 0"}} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Balance
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{formattedBalance(balance)}
|
||||
</Typography>
|
||||
|
||||
<Divider style={{margin: "16px 0"}} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Number of transactions
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{nonce}
|
||||
</Typography>
|
||||
|
||||
<Divider style={{margin: "16px 0"}} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Maximum transaction value
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{formattedBalance(maxTxValue)}
|
||||
</Typography>
|
||||
|
||||
<Divider style={{margin: "16px 0"}} />
|
||||
|
||||
{!toppingUp && <Button onClick={() => onTopUp(index, address)} size="large" color="primary" variant="contained" fullWidth>
|
||||
TOP UP 0.001 ETH
|
||||
</Button>}
|
||||
|
||||
{toppingUp && <div style={{marginTop: 50, textAlign: "center"}}>
|
||||
<CircularProgress />
|
||||
</div>}
|
||||
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
export default SelectedWalletDialog;
|
|
@ -16,26 +16,17 @@ const styles = {
|
|||
}
|
||||
};
|
||||
|
||||
const TopPanel = ({ wallets }) => {
|
||||
const totalWei = wallets.filter((wallet) => wallet).reduce((acc, w) => {
|
||||
return acc.add(new web3.utils.BN(w.value))
|
||||
}, new web3.utils.BN(0));
|
||||
|
||||
let unit = "wei",
|
||||
label = "wei",
|
||||
total = totalWei;
|
||||
|
||||
const totalEth = web3.utils.fromWei(total);
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<div>
|
||||
<Typography variant="h2" color="inherit">
|
||||
{totalEth} Ξ
|
||||
</Typography>
|
||||
</div>
|
||||
const TopPanel = ({ total, fullTotal }) => (
|
||||
<div style={styles.container}>
|
||||
<div>
|
||||
<Typography variant="h2" color="inherit">
|
||||
{total} Ξ
|
||||
</Typography>
|
||||
<Typography variant="body1" color="inherit" style={{textAlign: "center"}}>
|
||||
{fullTotal}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default TopPanel;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { newWallet } from '../actions';
|
|||
|
||||
import WalletsListItem from '../containers/WalletsListItem';
|
||||
|
||||
import TopPanel from './TopPanel';
|
||||
import TopPanel from '../containers/TopPanel';
|
||||
import List from '@material-ui/core/List';
|
||||
import Fab from '@material-ui/core/Fab';
|
||||
import AddIcon from '@material-ui/icons/Add';
|
||||
|
|
|
@ -21,27 +21,55 @@ const StyledListItemText = withStyles({
|
|||
},
|
||||
})(ListItemText);
|
||||
|
||||
const secondaryLineStyles = {
|
||||
display: "block"
|
||||
const formattedBalance = (balance) => {
|
||||
if (balance) {
|
||||
return web3.utils.fromWei(balance);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
const styles = {
|
||||
secondaryLine: {
|
||||
display: "block"
|
||||
},
|
||||
avatar: {
|
||||
color: "#1a1a1a",
|
||||
backgroundColor: '#fff',
|
||||
backgroundImage: 'linear-gradient(120deg, rgba(250, 112, 154, 0.5) 0%, rgba(254, 225, 64, 0.5) 100%)',
|
||||
boxShadow: "rgba(180, 180, 180, 0.6) 0 0 5px 1px",
|
||||
position: "relative",
|
||||
},
|
||||
avatarLoading: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
}
|
||||
};
|
||||
|
||||
const WalletsListItem = ({ wallet, onItemClick }) => {
|
||||
const secondary = <span>
|
||||
<span style={secondaryLineStyles}>🔖 {compressedAddress(wallet.address)}</span>
|
||||
<span style={secondaryLineStyles}>💳 {isEmptyAddress(wallet.keycard) ? "" : compressedAddress(wallet.keycard)}</span>
|
||||
<span style={styles.secondaryLine}>🔖 {compressedAddress(wallet.address, 8)}</span>
|
||||
<span style={styles.secondaryLine}>💳 {isEmptyAddress(wallet.keycard) ? "" : compressedAddress(wallet.keycard, 8)}</span>
|
||||
</span>;
|
||||
|
||||
const secondaryLoading = "loading..."
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ListItem button onClick={() => onItemClick()}>
|
||||
<ListItem button onClick={() => onItemClick(wallet.index)}>
|
||||
{!wallet.creating &&
|
||||
<React.Fragment>
|
||||
<Avatar>{wallet.icon}</Avatar>
|
||||
<StyledListItemText primary={wallet.value + " Ξ"} secondary={wallet.creating ? secondaryLoading : secondary} />
|
||||
<Avatar style={styles.avatar}>
|
||||
{wallet.toppingUp &&
|
||||
<CircularProgress color="secondary" style={styles.avatarLoading}/>}
|
||||
|
||||
{wallet.icon}
|
||||
</Avatar>
|
||||
<StyledListItemText primary={formattedBalance(wallet.balance) + " Ξ"} secondary={wallet.creating ? secondaryLoading : secondary} />
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
{wallet.creating && <CircularProgress color="secondary" />}
|
||||
</ListItem>
|
||||
<Divider />
|
||||
|
|
|
@ -4,6 +4,9 @@ import {
|
|||
newWalletSelectIcon,
|
||||
newWalletCancel,
|
||||
createWallet,
|
||||
newWalletFormKeycardAddressChanged,
|
||||
newWalletFormMaxTxValueChanged,
|
||||
signMessagePinless,
|
||||
} from '../actions';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
@ -11,12 +14,17 @@ const mapStateToProps = state => ({
|
|||
selected: state.newWalletForm.icon,
|
||||
creating: state.newWalletForm.creating,
|
||||
error: (state.newWalletForm.error || "").toString(),
|
||||
keycard: state.newWalletForm.keycardAddress || "",
|
||||
maxTxValue: state.newWalletForm.maxTxValue,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onIconClick: (icon) => dispatch(newWalletSelectIcon(icon)),
|
||||
onCancelClick: () => dispatch(newWalletCancel()),
|
||||
onCreateClick: () => dispatch(createWallet()),
|
||||
onKeycardChange: (address) => dispatch(newWalletFormKeycardAddressChanged(address)),
|
||||
onMaxTxValueChange: (value) => dispatch(newWalletFormMaxTxValueChanged(value)),
|
||||
onTapButtonClick: (message) => dispatch(signMessagePinless(message)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import { connect } from 'react-redux';
|
||||
import SelectedWalletDialog from '../components/SelectedWalletDialog';
|
||||
import {
|
||||
closeSelectedWallet,
|
||||
topUpWallet,
|
||||
} from '../actions';
|
||||
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const props = {
|
||||
open: state.selectedWallet.open,
|
||||
}
|
||||
|
||||
const index = state.selectedWallet.index;
|
||||
if (index == null || index == undefined) {
|
||||
return props;
|
||||
}
|
||||
|
||||
const wallet = state.wallets[index];
|
||||
|
||||
return {
|
||||
...props,
|
||||
icon: wallet.icon,
|
||||
address: wallet.address,
|
||||
nonce: wallet.nonce,
|
||||
keycardAddress: wallet.keycardAddress,
|
||||
balance: wallet.balance,
|
||||
index: wallet.index,
|
||||
toppingUp: wallet.toppingUp,
|
||||
maxTxValue: wallet.maxTxValue,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onCloseClick: () => dispatch(closeSelectedWallet()),
|
||||
onTopUp: (index, address) => dispatch(topUpWallet(index, address)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(SelectedWalletDialog);
|
|
@ -0,0 +1,28 @@
|
|||
import { connect } from 'react-redux';
|
||||
import TopPanel from '../components/TopPanel';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const totalWei = state.wallets.filter((wallet) => wallet).reduce((acc, w) => {
|
||||
return acc.add(new web3.utils.BN(w.balance))
|
||||
}, new web3.utils.BN(0));
|
||||
|
||||
const fullTotal = web3.utils.fromWei(totalWei);
|
||||
const parts = fullTotal.split(".");
|
||||
let total = parts[0];
|
||||
let decimals = (parts[1] || "").slice(0, 4)
|
||||
if (decimals.length > 0) {
|
||||
total = `${total}.${decimals}`;
|
||||
}
|
||||
|
||||
return {
|
||||
total: total,
|
||||
fullTotal: fullTotal,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TopPanel);
|
|
@ -1,14 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
import WalletsListItem from '../components/WalletsListItem';
|
||||
import { newWallet } from '../actions';
|
||||
|
||||
const VALID_NETWORK_ID = 3;
|
||||
import { selectWallet } from '../actions';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
//FIXME: hack
|
||||
wallets: state.wallets,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onItemClick: () => dispatch(newWallet())
|
||||
onItemClick: (index) => dispatch(selectWallet(index))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
|
|
@ -15,16 +15,34 @@ import {
|
|||
NEW_WALLET,
|
||||
NEW_WALLET_SELECT_ICON,
|
||||
NEW_WALLET_CANCEL,
|
||||
NEW_WALLET_FORM_KEYCARD_ADDRESS_CHANGED,
|
||||
NEW_WALLET_FORM_MAX_TX_VALUE_CHANGED,
|
||||
CREATING_WALLET,
|
||||
WALLET_CREATED,
|
||||
WALLET_CREATION_ERROR,
|
||||
SELECT_WALLET,
|
||||
CLOSE_SELECTED_WALLET,
|
||||
TOPPING_UP_WALLET,
|
||||
ERROR_TOPPING_UP_WALLET,
|
||||
WALLET_TOPPED_UP,
|
||||
KEYCARD_DISCOVERED,
|
||||
} from "../actions";
|
||||
|
||||
const newWalletFormInitialState = {
|
||||
open: false,
|
||||
icon: null,
|
||||
creating: false,
|
||||
error: null
|
||||
error: null,
|
||||
keycardAddress: null,
|
||||
index: null,
|
||||
balance: null,
|
||||
toppingUp: false,
|
||||
maxTxValue: "0.1",
|
||||
}
|
||||
|
||||
const selectedWalletInitialState = {
|
||||
open: false,
|
||||
index: null,
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
|
@ -39,11 +57,12 @@ const initialState = {
|
|||
loadedWalletsCount: 0,
|
||||
wallets: [],
|
||||
newWalletForm: newWalletFormInitialState,
|
||||
selectedWallet: selectedWalletInitialState,
|
||||
};
|
||||
|
||||
export default function(state, action) {
|
||||
console.log("state", state)
|
||||
console.log("action", action)
|
||||
console.log("state", state)
|
||||
|
||||
if (typeof state === 'undefined') {
|
||||
return initialState;
|
||||
|
@ -105,10 +124,13 @@ export default function(state, action) {
|
|||
case WALLET_LOADED:
|
||||
const wallet = {
|
||||
address: action.address,
|
||||
nonce: action.nonce,
|
||||
keycard: action.keycard,
|
||||
name: action.name,
|
||||
value: action.value,
|
||||
icon: action.icon
|
||||
balance: action.balance,
|
||||
icon: action.icon,
|
||||
index: action.index,
|
||||
maxTxValue: action.maxTxValue,
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
|
@ -140,13 +162,27 @@ export default function(state, action) {
|
|||
open: false
|
||||
}
|
||||
});
|
||||
case NEW_WALLET_FORM_KEYCARD_ADDRESS_CHANGED:
|
||||
return Object.assign({}, state, {
|
||||
newWalletForm: {
|
||||
...state.newWalletForm,
|
||||
keycardAddress: action.address
|
||||
}
|
||||
});
|
||||
case NEW_WALLET_FORM_MAX_TX_VALUE_CHANGED:
|
||||
return Object.assign({}, state, {
|
||||
newWalletForm: {
|
||||
...state.newWalletForm,
|
||||
maxTxValue: action.value
|
||||
}
|
||||
});
|
||||
case CREATING_WALLET:
|
||||
const tmpWallet = {
|
||||
creating: true,
|
||||
address: "",
|
||||
keycard: "",
|
||||
name: "",
|
||||
value: 0,
|
||||
balance: 0,
|
||||
icon: action.icon
|
||||
}
|
||||
|
||||
|
@ -173,6 +209,48 @@ export default function(state, action) {
|
|||
error: action.error
|
||||
}
|
||||
});
|
||||
case SELECT_WALLET:
|
||||
return Object.assign({}, state, {
|
||||
selectedWallet: {
|
||||
...state.selectedWallet,
|
||||
open: true,
|
||||
index: action.index,
|
||||
}
|
||||
});
|
||||
case CLOSE_SELECTED_WALLET:
|
||||
return Object.assign({}, state, {
|
||||
selectedWallet: selectedWalletInitialState,
|
||||
});
|
||||
case TOPPING_UP_WALLET:
|
||||
const toppingUpWallet = state.wallets[action.index];
|
||||
toppingUpWallet.toppingUp = true;
|
||||
|
||||
return Object.assign({}, state, {
|
||||
wallets: [
|
||||
...state.wallets.slice(0, action.index),
|
||||
toppingUpWallet,
|
||||
...state.wallets.slice(action.index + 1)
|
||||
],
|
||||
});
|
||||
case ERROR_TOPPING_UP_WALLET:
|
||||
case WALLET_TOPPED_UP:
|
||||
const toppedUpWallet = state.wallets[action.index];
|
||||
toppedUpWallet.toppingUp = false;
|
||||
|
||||
return Object.assign({}, state, {
|
||||
wallets: [
|
||||
...state.wallets.slice(0, action.index),
|
||||
toppedUpWallet,
|
||||
...state.wallets.slice(action.index + 1)
|
||||
],
|
||||
});
|
||||
case KEYCARD_DISCOVERED:
|
||||
return Object.assign({}, state, {
|
||||
newWalletForm: {
|
||||
...state.newWalletForm,
|
||||
keycardAddress: action.address,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
export const compressedAddress = (a) =>
|
||||
`${a.slice(0, 6)}...${a.slice(a.length - 4)}`
|
||||
export const emptyAddress = "0x0000000000000000000000000000000000000000"
|
||||
|
||||
export const compressedAddress = (a, padding) => {
|
||||
padding = padding || 4;
|
||||
return `${a.slice(0, padding + 2)}...${a.slice(a.length - padding)}`
|
||||
}
|
||||
|
||||
const emptyAddress = "0x0000000000000000000000000000000000000000"
|
||||
export const isEmptyAddress = (a) =>
|
||||
a == emptyAddress
|
||||
|
||||
|
|
|
@ -23,9 +23,11 @@ contract TapWallet {
|
|||
// anyone can add funds to the wallet
|
||||
function () external payable {}
|
||||
|
||||
constructor(bytes3 _name) public {
|
||||
constructor(bytes3 _name, address _keycard, uint256 _maxTxValue) public {
|
||||
owner = msg.sender;
|
||||
name = _name;
|
||||
keycard = _keycard;
|
||||
settings.maxTxValue = _maxTxValue;
|
||||
nonce = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ contract TapWalletFactory {
|
|||
bytes3 name
|
||||
);
|
||||
|
||||
function create(bytes3 name) public {
|
||||
TapWallet wallet = new TapWallet(name);
|
||||
function create(bytes3 name, address keycard, uint256 maxTxValue) public {
|
||||
TapWallet wallet = new TapWallet(name, keycard, maxTxValue);
|
||||
ownersWallets[msg.sender].push(address(wallet));
|
||||
emit NewWallet(wallet, name);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ contract('TapWalletFactory', () => {
|
|||
const ownerWalletsCountBefore = await TapWalletFactory.methods.ownerWalletsCount(owner).call();
|
||||
assert.equal(ownerWalletsCountBefore, 0);
|
||||
|
||||
const create = TapWalletFactory.methods.create("0x010203");
|
||||
const create = TapWalletFactory.methods.create("0x010203", zeroAddress, 0);
|
||||
const receipt = await create.send({
|
||||
from: owner
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ let owner,
|
|||
config({
|
||||
contracts: {
|
||||
TapWallet: {
|
||||
args: ["0x000000"]
|
||||
args: ["0x000000", "0x0000000000000000000000000000000000000000", 0]
|
||||
}
|
||||
}
|
||||
}, (err, _accounts) => {
|
||||
|
|
Loading…
Reference in New Issue