transaction list

This commit is contained in:
Michele Balistreri 2020-10-30 11:38:39 +01:00
parent e289bc2033
commit 5e6796f2d0
No known key found for this signature in database
GPG Key ID: E9567DA33A4F791A
7 changed files with 26 additions and 187 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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({

View File

@ -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;

View File

@ -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>

View File

@ -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);

View File

@ -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,