Merge pull request #14 from status-im/pos-keycard-api

simple-wallet: show payment requests
This commit is contained in:
Andrea Franz 2020-02-25 12:37:31 +01:00 committed by GitHub
commit 07b1c2ec6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 421 additions and 276 deletions

View File

@ -81,7 +81,7 @@ export const networkIDLoaded = (id) => ({
export const loadNetworkID = () => {
return (dispatch) => {
web3.eth.net.getId()
.then((id) => dispatch(networkIDLoaded(id)))
.then((id) => dispatch(networkIDLoaded(parseInt(id))))
.catch((err) => {
dispatch(web3Error(err))
})

View File

@ -18,6 +18,7 @@ export interface TxsLoadedAction {
export const TXS_TRANSACTION_DISCOVERED = "TXS_TRANSACTION_DISCOVERED";
export interface TxsTransactionDiscoveredAction {
type: typeof TXS_TRANSACTION_DISCOVERED
event: string
pending: boolean
id: string
transactionHash: string
@ -38,8 +39,9 @@ export type TxsActions =
TxsTransactionDiscoveredAction |
TxsTransactionConfirmedAction;
export const topUpDiscovered = (id: string, transactionHash: string, pending: boolean, from: string, to: string | undefined, value: string): TxsTransactionDiscoveredAction => ({
export const transactionDiscovered = (event: string, id: string, transactionHash: string, pending: boolean, from: string, to: string | undefined, value: string): TxsTransactionDiscoveredAction => ({
type: TXS_TRANSACTION_DISCOVERED,
event,
id,
transactionHash,
pending,
@ -81,6 +83,9 @@ export const watchPendingTransaction = (web3: Web3, dispatch: Dispatch, walletAd
export const loadTransactions = (web3: Web3, dispatch: Dispatch, getState: () => RootState, wallet: Contract) => {
const state = getState();
const walletAddress = state.wallet.walletAddress;
if (walletAddress === undefined) {
return;
}
dispatch(loadingTransactions());
wallet.getPastEvents('TopUp', {fromBlock: 0, toBlock: 'latest'}).then((events: any) => {
@ -88,7 +93,7 @@ export const loadTransactions = (web3: Web3, dispatch: Dispatch, getState: () =>
//FIXME: use the right type for event
events.forEach((event: any) => {
const values = event.returnValues;
dispatch(topUpDiscovered(event.id, event.transactionHash, false, values.from, walletAddress, values.value));
dispatch(transactionDiscovered("TopUp", event.id, event.transactionHash, false, values.from, walletAddress, values.value));
});
dispatch(transactionsLoaded());
}).catch(error => {
@ -96,10 +101,29 @@ export const loadTransactions = (web3: Web3, dispatch: Dispatch, getState: () =>
console.log("error", error)
});
wallet.getPastEvents('NewPaymentRequest', {fromBlock: 0, toBlock: 'latest'}).then((events: any) => {
//FIXME: add loading event
//FIXME: use the right type for event
events.forEach((event: any) => {
const values = event.returnValues;
dispatch(transactionDiscovered("NewPaymentRequest", event.id, event.transactionHash, false, walletAddress, values.to, values.amount));
});
// dispatch(transactionsLoaded());
}).catch(error => {
//FIXME: handle error
console.log("error", error)
});
web3.eth.getBlockNumber().then((blockNumber: number) => {
wallet.events.TopUp({fromBlock: blockNumber}).on('data', (event: any) => {
const values = event.returnValues;
dispatch(topUpDiscovered(event.id, event.transactionHash, true, values.from, walletAddress, values.value));
dispatch(transactionDiscovered("TopUp", event.id, event.transactionHash, true, values.from, walletAddress, values.value));
watchPendingTransaction(web3, dispatch, walletAddress, event.transactionHash);
})
wallet.events.NewPaymentRequest({fromBlock: blockNumber}).on('data', (event: any) => {
const values = event.returnValues;
dispatch(transactionDiscovered("NewPaymentRequest", event.id, event.transactionHash, true, walletAddress, values.to, values.amount));
watchPendingTransaction(web3, dispatch, walletAddress, event.transactionHash);
})
});

View File

@ -8,6 +8,8 @@ import { loadWallet } from './wallet';
export const VALID_NETWORK_NAME = "Ropsten";
export const VALID_NETWORK_ID = 3;
// export const VALID_NETWORK_NAME = "Goerli";
// export const VALID_NETWORK_ID = 5;
export const LOCAL_NETWORK_ID = 1337;
@ -70,7 +72,7 @@ export const initializeWeb3 = () => {
const t: Web3Type = w.ethereum.isStatus ? Web3Type.Status : Web3Type.Generic;
dispatch(web3Initialized(t));
config.web3!.eth.net.getId().then((id: number) => {
if (id !== VALID_NETWORK_ID) {
if (id !== VALID_NETWORK_ID && id !== LOCAL_NETWORK_ID) {
dispatch(web3Error(`wrong network, please connect to ${VALID_NETWORK_NAME}`));
return;
}

View File

@ -43,6 +43,7 @@ const WalletsList = (props: Props) => {
{props.transactions.map((tx) => (
<TransactionsListItem key={tx.id}
pending={tx.pending}
event={tx.event}
from={tx.from}
to={tx.to}
valueInETH={tx.valueInETH}

View File

@ -1,6 +1,8 @@
import React from 'react'
import { makeStyles } from '@material-ui/core/styles';
import TransactionInIcon from '@material-ui/icons/CallReceived';
import TransactionOutIcon from '@material-ui/icons/CallMade';
import TransactionUnknownIcon from '@material-ui/icons/HelpOutline';
import { withStyles } from '@material-ui/core/styles';
import ListItem from '@material-ui/core/ListItem';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
@ -8,11 +10,13 @@ import Avatar from '@material-ui/core/Avatar';
import ListItemText from '@material-ui/core/ListItemText';
import Divider from '@material-ui/core/Divider';
import CircularProgress from '@material-ui/core/CircularProgress';
import Fade from '@material-ui/core/Fade';
import {
compressedAddress,
} from '../utils';
export interface Props {
event: string
transactionHash: string
pending: boolean | undefined
from: string | undefined
@ -34,19 +38,49 @@ const useStyles = makeStyles(theme => ({
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",
backgroundImage: 'linear-gradient(120deg, rgba(114, 255, 132, 0.5) 0%, rgba(255, 225, 255, 0.5) 100%)',
},
avatarIn: {
backgroundColor: '#fff',
boxShadow: "rgba(180, 180, 180, 0.6) 0 0 5px 1px",
extend: "avatar",
backgroundImage: 'linear-gradient(120deg, rgba(114, 255, 132, 0.5) 0%, rgba(255, 225, 255, 0.5) 100%)',
},
avatarOut: {
backgroundColor: '#fff',
boxShadow: "rgba(180, 180, 180, 0.6) 0 0 5px 1px",
extend: "avatar",
backgroundImage: 'linear-gradient(120deg, rgba(255, 114, 114, 0.5) 0%, rgba(255, 225, 255, 0.5) 100%)',
},
avatarLoading: {
position: "absolute",
top: 0,
left: 0,
}
},
icon: {
color: "#000",
},
iconIn: {
color: "green",
},
iconOut: {
color: "red",
},
}));
const icon = (event: string, className: any) => {
switch(event) {
case "TopUp":
return <TransactionInIcon className={className} />
case "NewPaymentRequest":
return <TransactionOutIcon className={className} />
default:
return <TransactionUnknownIcon />
}
};
const TransactionsListItem = (props: Props) => {
const classes = useStyles();
const fromAddress = props.from ? compressedAddress(props.from, 8) : "";
@ -57,14 +91,28 @@ const TransactionsListItem = (props: Props) => {
<span className={classes.secondaryLine}>to: {toAddress}</span>
</span>;
const [avatarClass, iconClass] = (event => {
switch(event) {
case "TopUp":
return [classes.avatarIn, classes.iconIn];
case "NewPaymentRequest":
return [classes.avatarOut, classes.iconOut];
default:
return [classes.avatar, classes.icon];
}
})(props.event)
return (
<>
<ListItem button>
<ListItemAvatar>
<Avatar className={classes.avatar}>
{(props.pending === true || props.pending === undefined) && <CircularProgress color="secondary" className={classes.avatarLoading}/>}
<TransactionInIcon />
</Avatar>
<Fade in={true} timeout={800}>
<Avatar className={avatarClass}>
{(props.pending === true || props.pending === undefined)
&& <CircularProgress color="secondary" className={classes.avatarLoading}/>}
{icon(props.event, iconClass)}
</Avatar>
</Fade>
</ListItemAvatar>
<StyledListItemText primary={`${props.valueInETH} Ξ`} secondary={secondary} />
</ListItem>

View File

@ -1,263 +1,330 @@
import { AbiItem } from 'web3-utils';
export const abi: AbiItem[] = [
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "bytes3"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "hash",
"type": "bytes32"
},
{
"name": "sig",
"type": "bytes"
}
],
"name": "recover",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_keycard",
"type": "address"
}
],
"name": "setKeycard",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "withdraw",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "keycard",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_maxTxValue",
"type": "uint256"
}
],
"name": "setSettings",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "nonce",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_hashToSign",
"type": "bytes32"
},
{
"name": "_signature",
"type": "bytes"
},
{
"name": "_nonce",
"type": "uint256"
},
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "requestPayment",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "settings",
"outputs": [
{
"name": "maxTxValue",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "pendingWithdrawals",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"name": "_name",
"type": "bytes3"
},
{
"name": "_keycard",
"type": "address"
},
{
"name": "_maxTxValue",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "from",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "TopUp",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "nonce",
"type": "uint256"
},
{
"indexed": false,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "NewPaymentRequest",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "NewWithdrawal",
"type": "event"
}
{
"constant": false,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "setOwner",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "register",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{
"name": "maxTxValue",
"type": "uint256"
},
{
"name": "minBlockDistance",
"type": "uint256"
}
],
"name": "_settings",
"type": "tuple"
}
],
"name": "setSettings",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_keycard",
"type": "address"
}
],
"name": "setKeycard",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "lastUsedBlockNum",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "withdraw",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_register",
"type": "address"
}
],
"name": "setRegister",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "keycard",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{
"name": "blockNumber",
"type": "uint256"
},
{
"name": "blockHash",
"type": "bytes32"
},
{
"name": "amount",
"type": "uint256"
},
{
"name": "to",
"type": "address"
}
],
"name": "_payment",
"type": "tuple"
},
{
"name": "_signature",
"type": "bytes"
}
],
"name": "requestPayment",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalPendingWithdrawals",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "availableBalance",
"outputs": [
{
"name": "",
"type": "int256"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "settings",
"outputs": [
{
"name": "maxTxValue",
"type": "uint256"
},
{
"name": "minBlockDistance",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "pendingWithdrawals",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"name": "_owner",
"type": "address"
},
{
"name": "_keycard",
"type": "address"
},
{
"components": [
{
"name": "maxTxValue",
"type": "uint256"
},
{
"name": "minBlockDistance",
"type": "uint256"
}
],
"name": "_settings",
"type": "tuple"
},
{
"name": "_register",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "from",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "TopUp",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "blockNumber",
"type": "uint256"
},
{
"indexed": false,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "amount",
"type": "uint256"
}
],
"name": "NewPaymentRequest",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "NewWithdrawal",
"type": "event"
}
]

View File

@ -9,6 +9,7 @@ import {
export interface TransactionState {
id: string
event: string
transactionHash: string
pending: boolean | undefined
from: string | undefined
@ -26,6 +27,7 @@ export interface TransactionsState {
const newTransactionState = (): TransactionState => ({
id: "",
event: "",
transactionHash: "",
pending: undefined,
from: undefined,
@ -75,6 +77,7 @@ export const transactionsReducer = (state: TransactionsState = initialState, act
[action.transactionHash]: {
...txState,
id: action.id,
event: action.event,
transactionHash: action.transactionHash,
pending: action.pending,
from: action.from,

View File

@ -17,10 +17,10 @@ import App from './containers/App';
const logger = (store) => {
return (next) => {
return (action) => {
console.log('dispatching\n', action);
const result = next(action);
console.log('next state\n', store.getState());
return result;
console.log('dispatching\n', action);
const result = next(action);
console.log('next state\n', store.getState());
return result;
}
}
};