get past and new Transfer events for in/out txs

This commit is contained in:
Andrea Franz 2020-03-12 12:48:13 +01:00
parent c1ec94fa6c
commit affa0582a1
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
6 changed files with 94 additions and 58 deletions

View File

@ -3,7 +3,7 @@ import { RootState } from '../reducers';
import { Contract } from 'web3-eth-contract'; import { Contract } from 'web3-eth-contract';
import Web3 from 'web3'; import Web3 from 'web3';
import { TransactionReceipt } from 'web3-core'; import { TransactionReceipt } from 'web3-core';
// import { loadBalance } from './wallet'; import { loadWalletBalance } from './wallet';
import { loadBlock } from './blocks'; import { loadBlock } from './blocks';
import { abi as keycardWalletABI } from '../contracts/KeycardWallet'; import { abi as keycardWalletABI } from '../contracts/KeycardWallet';
import { addPadding } from "../utils"; import { addPadding } from "../utils";
@ -68,23 +68,6 @@ export const transactionConfirmed = (transactionHash: string): TxsTransactionCon
transactionHash, transactionHash,
}); });
export const watchPendingTransaction = (web3: Web3, dispatch: Dispatch, walletAddress: string | undefined, wallet: Contract, transactionHash: string) => {
web3.eth.getTransactionReceipt(transactionHash).then((tx: TransactionReceipt) => {
if (tx.status) {
dispatch(transactionConfirmed(transactionHash));
if (walletAddress !== undefined) {
// dispatch<any>(loadBalance(web3, walletAddress, wallet));
}
return;
}
window.setTimeout(() => watchPendingTransaction(web3, dispatch, walletAddress, wallet, transactionHash), 5000)
}).catch((error: string) => {
//FIXME: handle error
console.log("error", error)
});
}
export const loadTransactions = (web3: Web3, erc20: Contract) => { export const loadTransactions = (web3: Web3, erc20: Contract) => {
return (dispatch: Dispatch, getState: () => RootState) => { return (dispatch: Dispatch, getState: () => RootState) => {
const state = getState(); const state = getState();
@ -93,38 +76,87 @@ export const loadTransactions = (web3: Web3, erc20: Contract) => {
return; return;
} }
const wallet = new web3.eth.Contract(keycardWalletABI, walletAddress);
const topic = web3.utils.sha3("Transfer(address,address,uint256)");
const options = {
fromBlock: 0,
toBlock: "latest",
topics: [
topic,
null,
addPadding(64, walletAddress),
]
};
erc20.getPastEvents("allEvents", options).then((events: any) => {
events.forEach((event: any) => {
const values = event.returnValues;
dispatch<any>(loadBlock(event.blockNumber));
dispatch(transactionDiscovered("TopUp", event.id, event.blockNumber, event.transactionHash, false, values.from, walletAddress, values.value));
});
dispatch(transactionsLoaded());
});
dispatch(loadingTransactions());
const filter = {
to: walletAddress,
};
web3.eth.getBlockNumber().then((blockNumber: number) => { web3.eth.getBlockNumber().then((blockNumber: number) => {
erc20.events.Transfer({filter: filter}).on('data', (event: any) => { dispatch<any>(getPastTransactions(web3, erc20, walletAddress, blockNumber, "to"))
const values = event.returnValues; dispatch<any>(getPastTransactions(web3, erc20, walletAddress, blockNumber, "from"))
dispatch(transactionDiscovered("TopUp", event.id, event.blockNumber, event.transactionHash, true, values.from, walletAddress, values.value));
watchPendingTransaction(web3, dispatch, walletAddress, wallet, event.transactionHash); dispatch<any>(subscribeToTransactions(web3, erc20, walletAddress, blockNumber, "to"))
}) dispatch<any>(subscribeToTransactions(web3, erc20, walletAddress, blockNumber, "from"))
}); });
}; };
} }
type ToOrFrom = "to" | "from";
const getPastTransactions = (web3: Web3, erc20: Contract, walletAddress: string, fromBlock: number, toOrFrom: ToOrFrom) => {
return (dispatch: Dispatch, getState: () => RootState) => {
const paddedWalletAddress = addPadding(64, walletAddress);
const eventType = toOrFrom == "to" ? "TopUp" : "NewPaymentRequest";
const topics: (null | string)[] = [null, null, null];
switch(toOrFrom) {
case "from":
topics[1] = paddedWalletAddress;
break;
case "to":
topics[2] = paddedWalletAddress;
break;
}
const options = {
fromBlock: 0,
toBlock: "latest",
topics: topics,
};
dispatch(loadingTransactions());
erc20.getPastEvents("Transfer", options).then((events: any) => {
events.forEach((event: any) => {
const values = event.returnValues;
dispatch<any>(loadBlock(event.blockNumber));
dispatch(transactionDiscovered(eventType, event.id, event.blockNumber, event.transactionHash, false, values.from, walletAddress, values.value));
});
dispatch(transactionsLoaded());
});
};
};
const subscribeToTransactions = (web3: Web3, erc20: Contract, walletAddress: string, fromBlock: number, toOrFrom: ToOrFrom) => {
return (dispatch: Dispatch, getState: () => RootState) => {
const options = {
fromBlock: fromBlock,
filter: {
[toOrFrom]: walletAddress,
}
};
const eventType = toOrFrom == "to" ? "TopUp" : "NewPaymentRequest";
erc20.events.Transfer(options).on('data', (event: any) => {
const values = event.returnValues;
const pending = event.blockHash === null;
dispatch(transactionDiscovered(eventType, event.id, event.blockNumber, event.transactionHash, pending, values.from, walletAddress, values.value));
dispatch<any>(loadWalletBalance(web3, undefined));
if (pending) {
watchPendingTransaction(web3, dispatch, walletAddress, event.transactionHash);
}
});
};
};
export const watchPendingTransaction = (web3: Web3, dispatch: Dispatch, walletAddress: string | undefined, transactionHash: string) => {
web3.eth.getTransactionReceipt(transactionHash).then((tx: TransactionReceipt) => {
if (tx.status) {
dispatch(transactionConfirmed(transactionHash));
if (walletAddress !== undefined) {
dispatch<any>(loadWalletBalance(web3, undefined));
}
return;
}
window.setTimeout(() => watchPendingTransaction(web3, dispatch, walletAddress, transactionHash), 5000)
}).catch((error: string) => {
//FIXME: handle error
console.log("error", error)
});
}

View File

@ -279,10 +279,14 @@ const loadERC20Symbol = (web3: Web3, erc20: Contract) => {
} }
} }
const loadWalletBalance = (web3: Web3, erc20: Contract) => { export const loadWalletBalance = (web3: Web3, _erc20: Contract | undefined) => {
return async (dispatch: Dispatch, getState: () => RootState) => { return async (dispatch: Dispatch, getState: () => RootState) => {
const address = getState().wallet.walletAddress!; const state = getState();
const address = state.wallet.walletAddress!;
const erc20 = _erc20 !== undefined ? _erc20 : new web3.eth.Contract(erc20DetailedABI, state.wallet.erc20Address!);
dispatch(loadingWalletBalance(address)); dispatch(loadingWalletBalance(address));
return erc20.methods.balanceOf(address).call().then((balance: string) => { return erc20.methods.balanceOf(address).call().then((balance: string) => {
dispatch(balanceLoaded(balance, balance)); dispatch(balanceLoaded(balance, balance));
return erc20; return erc20;

View File

@ -12,7 +12,6 @@ export const VALID_NETWORK_ID = 3;
// export const VALID_NETWORK_ID = 5; // export const VALID_NETWORK_ID = 5;
export const LOCAL_NETWORK_ID = 1337; export const LOCAL_NETWORK_ID = 1337;
enum Web3Type { enum Web3Type {
Generic, Generic,
Remote, Remote,

View File

@ -39,7 +39,8 @@ const WalletsList = (props: Props) => {
{props.loading && <div className={classes.loading}> {props.loading && <div className={classes.loading}>
<CircularProgress className={classes.progress} disableShrink></CircularProgress> <CircularProgress className={classes.progress} disableShrink></CircularProgress>
</div>} </div>}
{!props.loading && <List>
{<List>
{props.transactions.map((tx) => ( {props.transactions.map((tx) => (
<TransactionsListItem key={tx.id} id={tx.id} /> <TransactionsListItem key={tx.id} id={tx.id} />
))} ))}

View File

@ -33,7 +33,7 @@ const mapStateToProps = (state: RootState): StateProps => {
return { return {
...props, ...props,
loading: state.transactions.loading, loading: state.transactions.loadingRequests > 0,
transactions: transactions, transactions: transactions,
} }
}; };

View File

@ -20,7 +20,7 @@ export interface TransactionState {
} }
export interface TransactionsState { export interface TransactionsState {
loading: boolean loadingRequests: number
transactions: { transactions: {
[txHash: string]: TransactionState [txHash: string]: TransactionState
} }
@ -39,7 +39,7 @@ const newTransactionState = (): TransactionState => ({
}); });
const initialState: TransactionsState = { const initialState: TransactionsState = {
loading: false, loadingRequests: 0,
transactions: {}, transactions: {},
}; };
@ -48,14 +48,14 @@ export const transactionsReducer = (state: TransactionsState = initialState, act
case TXS_LOADING: { case TXS_LOADING: {
return { return {
...state, ...state,
loading: true, loadingRequests: state.loadingRequests + 1,
} }
} }
case TXS_LOADED: { case TXS_LOADED: {
return { return {
...state, ...state,
loading: false, loadingRequests: state.loadingRequests - 1,
} }
} }