transaction list
This commit is contained in:
parent
e289bc2033
commit
5e6796f2d0
|
@ -11,9 +11,9 @@ import "./BlockConsumer.sol";
|
||||||
import "./EVMUtils.sol";
|
import "./EVMUtils.sol";
|
||||||
|
|
||||||
contract StatusPay is BlockConsumer {
|
contract StatusPay is BlockConsumer {
|
||||||
event NewPayment(address from, address to, uint256 amount);
|
event NewPayment(address indexed from, address indexed to, uint256 amount);
|
||||||
event TopUp(address account, uint256 amount);
|
event TopUp(address indexed to, uint256 amount);
|
||||||
event Withdraw(address account, uint256 amount);
|
event Withdraw(address indexed from, uint256 amount);
|
||||||
|
|
||||||
struct Payment {
|
struct Payment {
|
||||||
uint256 blockNumber;
|
uint256 blockNumber;
|
||||||
|
|
|
@ -5,7 +5,8 @@ import Web3 from 'web3';
|
||||||
import { TransactionReceipt } from 'web3-core';
|
import { TransactionReceipt } from 'web3-core';
|
||||||
import { loadWalletBalance } from './wallet';
|
import { loadWalletBalance } from './wallet';
|
||||||
import { loadBlock } from './blocks';
|
import { loadBlock } from './blocks';
|
||||||
import { addPadding } from "../utils";
|
|
||||||
|
type ToOrFrom = "to" | "from";
|
||||||
|
|
||||||
export const TXS_LOADING = "TXS_LOADING";
|
export const TXS_LOADING = "TXS_LOADING";
|
||||||
export interface TxsLoadingAction {
|
export interface TxsLoadingAction {
|
||||||
|
@ -20,6 +21,7 @@ export interface TxsLoadedAction {
|
||||||
export const TXS_TRANSACTION_DISCOVERED = "TXS_TRANSACTION_DISCOVERED";
|
export const TXS_TRANSACTION_DISCOVERED = "TXS_TRANSACTION_DISCOVERED";
|
||||||
export interface TxsTransactionDiscoveredAction {
|
export interface TxsTransactionDiscoveredAction {
|
||||||
type: typeof TXS_TRANSACTION_DISCOVERED
|
type: typeof TXS_TRANSACTION_DISCOVERED
|
||||||
|
direction: ToOrFrom
|
||||||
event: string
|
event: string
|
||||||
pending: boolean
|
pending: boolean
|
||||||
id: string
|
id: string
|
||||||
|
@ -42,8 +44,9 @@ export type TxsActions =
|
||||||
TxsTransactionDiscoveredAction |
|
TxsTransactionDiscoveredAction |
|
||||||
TxsTransactionConfirmedAction;
|
TxsTransactionConfirmedAction;
|
||||||
|
|
||||||
export const transactionDiscovered = (event: string, id: string, blockNumber: number, transactionHash: string, pending: boolean, from: string, to: string | undefined, value: string): TxsTransactionDiscoveredAction => ({
|
export const transactionDiscovered = (direction: ToOrFrom, event: string, id: string, blockNumber: number, transactionHash: string, pending: boolean, from: string | undefined, to: string | undefined, value: string): TxsTransactionDiscoveredAction => ({
|
||||||
type: TXS_TRANSACTION_DISCOVERED,
|
type: TXS_TRANSACTION_DISCOVERED,
|
||||||
|
direction,
|
||||||
event,
|
event,
|
||||||
id,
|
id,
|
||||||
blockNumber,
|
blockNumber,
|
||||||
|
@ -85,42 +88,27 @@ export const loadTransactions = (web3: Web3, statusPay: Contract) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type ToOrFrom = "to" | "from";
|
|
||||||
|
|
||||||
const getPastTransactions = (web3: Web3, statusPay: Contract, walletAddress: string, fromBlock: number, toOrFrom: ToOrFrom) => {
|
const getPastTransactions = (web3: Web3, statusPay: Contract, walletAddress: string, fromBlock: number, toOrFrom: ToOrFrom) => {
|
||||||
return (dispatch: Dispatch, getState: () => RootState) => {
|
return (dispatch: Dispatch, getState: () => RootState) => {
|
||||||
const paddedWalletAddress = addPadding(64, walletAddress);
|
|
||||||
const eventType = toOrFrom === "to" ? "TopUp" : "NewPayment";
|
|
||||||
const topics: (null | string)[] = [null, null, null];
|
|
||||||
|
|
||||||
switch(toOrFrom) {
|
|
||||||
case "from":
|
|
||||||
topics[1] = paddedWalletAddress;
|
|
||||||
break;
|
|
||||||
case "to":
|
|
||||||
topics[2] = paddedWalletAddress;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
fromBlock: 0,
|
fromBlock: 0,
|
||||||
toBlock: "latest",
|
toBlock: "latest",
|
||||||
topics: topics,
|
filter: { [toOrFrom]: walletAddress}
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch(loadingTransactions());
|
dispatch(loadingTransactions());
|
||||||
statusPay.getPastEvents("Transfer", options).then((events: any) => {
|
statusPay.getPastEvents("allEvents", options).then((events: any) => {
|
||||||
events.forEach((event: any) => {
|
events.forEach((event: any) => {
|
||||||
const values = event.returnValues;
|
const values = event.returnValues;
|
||||||
dispatch<any>(loadBlock(event.blockNumber));
|
dispatch<any>(loadBlock(event.blockNumber));
|
||||||
dispatch(transactionDiscovered(eventType, event.id, event.blockNumber, event.transactionHash, false, values.from, walletAddress, values.value));
|
dispatch(transactionDiscovered(toOrFrom, event.event, event.id, event.blockNumber, event.transactionHash, false, values.from, values.to, values.value));
|
||||||
});
|
});
|
||||||
dispatch(transactionsLoaded());
|
dispatch(transactionsLoaded());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const subscribeToTransactions = (web3: Web3, erc20: Contract, walletAddress: string, fromBlock: number, toOrFrom: ToOrFrom) => {
|
const subscribeToTransactions = (web3: Web3, statusPay: Contract, walletAddress: string, fromBlock: number, toOrFrom: ToOrFrom) => {
|
||||||
return (dispatch: Dispatch, getState: () => RootState) => {
|
return (dispatch: Dispatch, getState: () => RootState) => {
|
||||||
const options = {
|
const options = {
|
||||||
fromBlock: fromBlock,
|
fromBlock: fromBlock,
|
||||||
|
@ -129,12 +117,10 @@ const subscribeToTransactions = (web3: Web3, erc20: Contract, walletAddress: str
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const eventType = toOrFrom === "to" ? "TopUp" : "NewPayment";
|
statusPay.events.allEvents(options).on('data', (event: any) => {
|
||||||
|
|
||||||
erc20.events.Transfer(options).on('data', (event: any) => {
|
|
||||||
const values = event.returnValues;
|
const values = event.returnValues;
|
||||||
const pending = event.blockHash === null;
|
const pending = event.blockHash === null;
|
||||||
dispatch(transactionDiscovered(eventType, event.id, event.blockNumber, event.transactionHash, pending, values.from, walletAddress, values.value));
|
dispatch(transactionDiscovered(toOrFrom, event.event, event.id, event.blockNumber, event.transactionHash, pending, values.from, values.to, values.amount));
|
||||||
dispatch<any>(loadWalletBalance(web3, undefined));
|
dispatch<any>(loadWalletBalance(web3, undefined));
|
||||||
if (pending) {
|
if (pending) {
|
||||||
watchPendingTransaction(web3, dispatch, walletAddress, event.transactionHash);
|
watchPendingTransaction(web3, dispatch, walletAddress, event.transactionHash);
|
||||||
|
|
|
@ -217,7 +217,7 @@ export const loadWallet = async (web3: Web3, dispatch: Dispatch, getState: () =>
|
||||||
const loadWalletAddress = (web3: Web3, statusPay: Contract, keycardAddress: string) => {
|
const loadWalletAddress = (web3: Web3, statusPay: Contract, keycardAddress: string) => {
|
||||||
return async (dispatch: Dispatch) => {
|
return async (dispatch: Dispatch) => {
|
||||||
dispatch(loadingWalletAddress(keycardAddress));
|
dispatch(loadingWalletAddress(keycardAddress));
|
||||||
return statusPay.methods.keycards(keycardAddress).call().then((address: string) => {
|
return statusPay.methods.resolveAccount(keycardAddress).call().then((address: string) => {
|
||||||
if (isEmptyAddress(address)) {
|
if (isEmptyAddress(address)) {
|
||||||
dispatch(keycardNotFound(address));
|
dispatch(keycardNotFound(address));
|
||||||
throw({
|
throw({
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
// 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 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 = ["💳", "👛", "💸", "😺"]
|
|
||||||
|
|
||||||
// const iconStyles = {
|
|
||||||
// fontSize: "2em",
|
|
||||||
// marginRight: 16,
|
|
||||||
// padding: 4,
|
|
||||||
// cursor: "pointer"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const selectedIconStyles = {
|
|
||||||
// ...iconStyles,
|
|
||||||
// border: "4px solid #999"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const errorStyles = {
|
|
||||||
// color: "red",
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function Transition(props) {
|
|
||||||
// return <Slide direction="up" {...props} />;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const NewWalletDialog = ({open, creating, selected, keycardAddress, onIconClick, onCancelClick, onCreateClick, onKeycardChange, error, onTapButtonClick, onMaxTxValueChange, maxTxValue}) => (
|
|
||||||
// <Dialog
|
|
||||||
// fullScreen
|
|
||||||
// TransitionComponent={Transition}
|
|
||||||
// open={open}
|
|
||||||
// aria-labelledby="alert-dialog-title"
|
|
||||||
// aria-describedby="alert-dialog-description"
|
|
||||||
// >
|
|
||||||
// <DialogTitle id="alert-dialog-title">New Wallet</DialogTitle>
|
|
||||||
// <DialogContent>
|
|
||||||
// <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={keycardAddress}
|
|
||||||
// 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">
|
|
||||||
// Cancel
|
|
||||||
// </Button>}
|
|
||||||
|
|
||||||
// <Button onClick={onCreateClick} color="primary" autoFocus disabled={(selected && !creating) ? false : true}>
|
|
||||||
// {!creating ? "Create" : "Creating..."}
|
|
||||||
// </Button>
|
|
||||||
// </DialogActions>
|
|
||||||
|
|
||||||
// {error &&
|
|
||||||
// <DialogActions>
|
|
||||||
// <div style={errorStyles}>{error}</div>
|
|
||||||
// </DialogActions>}
|
|
||||||
// </Dialog>
|
|
||||||
// );
|
|
||||||
|
|
||||||
// export default NewWalletDialog;
|
|
|
@ -69,10 +69,9 @@ const useStyles = makeStyles(theme => ({
|
||||||
|
|
||||||
const icon = (event: string, className: any) => {
|
const icon = (event: string, className: any) => {
|
||||||
switch(event) {
|
switch(event) {
|
||||||
case "TopUp":
|
case "to":
|
||||||
return <TransactionInIcon className={className} />
|
return <TransactionInIcon className={className} />
|
||||||
case "NewPayment":
|
case "from":
|
||||||
case "Withdraw":
|
|
||||||
return <TransactionOutIcon className={className} />
|
return <TransactionOutIcon className={className} />
|
||||||
default:
|
default:
|
||||||
return <TransactionUnknownIcon />
|
return <TransactionUnknownIcon />
|
||||||
|
@ -120,17 +119,16 @@ const TransactionsListItem = (props: Props) => {
|
||||||
<span className={classes.block}>to: {toAddress}</span>
|
<span className={classes.block}>to: {toAddress}</span>
|
||||||
</span>;
|
</span>;
|
||||||
|
|
||||||
const [avatarClass, iconClass] = (event => {
|
const [avatarClass, iconClass] = (direction => {
|
||||||
switch(event) {
|
switch(direction) {
|
||||||
case "TopUp":
|
case "to":
|
||||||
return [classes.avatarIn, classes.iconIn];
|
return [classes.avatarIn, classes.iconIn];
|
||||||
case "NewPayment":
|
case "from":
|
||||||
case "Withdraw":
|
|
||||||
return [classes.avatarOut, classes.iconOut];
|
return [classes.avatarOut, classes.iconOut];
|
||||||
default:
|
default:
|
||||||
return [classes.avatar, classes.icon];
|
return [classes.avatar, classes.icon];
|
||||||
}
|
}
|
||||||
})(tx.event)
|
})(tx.direction)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -140,7 +138,7 @@ const TransactionsListItem = (props: Props) => {
|
||||||
<Avatar className={avatarClass}>
|
<Avatar className={avatarClass}>
|
||||||
{(tx.pending === true || tx.pending === undefined)
|
{(tx.pending === true || tx.pending === undefined)
|
||||||
&& <CircularProgress color="secondary" className={classes.avatarLoading}/>}
|
&& <CircularProgress color="secondary" className={classes.avatarLoading}/>}
|
||||||
{icon(tx.event, iconClass)}
|
{icon(tx.direction, iconClass)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</Fade>
|
</Fade>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
// import { connect } from 'react-redux';
|
|
||||||
// import NewWalletDialog from '../components/NewWalletDialog';
|
|
||||||
// import {
|
|
||||||
// newWalletSelectIcon,
|
|
||||||
// newWalletCancel,
|
|
||||||
// createWallet,
|
|
||||||
// newWalletFormKeycardAddressChanged,
|
|
||||||
// newWalletFormMaxTxValueChanged,
|
|
||||||
// signMessagePinless,
|
|
||||||
// } from '../actions';
|
|
||||||
|
|
||||||
// const mapStateToProps = state => ({
|
|
||||||
// open: state.newWalletForm.open,
|
|
||||||
// selected: state.newWalletForm.icon,
|
|
||||||
// creating: state.newWalletForm.creating,
|
|
||||||
// error: (state.newWalletForm.error || "").toString(),
|
|
||||||
// keycardAddress: 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(
|
|
||||||
// mapStateToProps,
|
|
||||||
// mapDispatchToProps
|
|
||||||
// )(NewWalletDialog);
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
export interface TransactionState {
|
export interface TransactionState {
|
||||||
id: string
|
id: string
|
||||||
blockNumber: number
|
blockNumber: number
|
||||||
|
direction: string
|
||||||
event: string
|
event: string
|
||||||
transactionHash: string
|
transactionHash: string
|
||||||
pending: boolean | undefined
|
pending: boolean | undefined
|
||||||
|
@ -29,6 +30,7 @@ export interface TransactionsState {
|
||||||
const newTransactionState = (): TransactionState => ({
|
const newTransactionState = (): TransactionState => ({
|
||||||
id: "",
|
id: "",
|
||||||
blockNumber: 0,
|
blockNumber: 0,
|
||||||
|
direction: "",
|
||||||
event: "",
|
event: "",
|
||||||
transactionHash: "",
|
transactionHash: "",
|
||||||
pending: undefined,
|
pending: undefined,
|
||||||
|
@ -80,6 +82,7 @@ export const transactionsReducer = (state: TransactionsState = initialState, act
|
||||||
...txState,
|
...txState,
|
||||||
id: action.id,
|
id: action.id,
|
||||||
blockNumber: action.blockNumber,
|
blockNumber: action.blockNumber,
|
||||||
|
direction: action.direction,
|
||||||
event: action.event,
|
event: action.event,
|
||||||
transactionHash: action.transactionHash,
|
transactionHash: action.transactionHash,
|
||||||
pending: action.pending,
|
pending: action.pending,
|
||||||
|
|
Loading…
Reference in New Issue