From affa0582a1d18a0b7a91f48ebc3f01851eb99cb0 Mon Sep 17 00:00:00 2001 From: Andrea Franz Date: Thu, 12 Mar 2020 12:48:13 +0100 Subject: [PATCH] get past and new Transfer events for in/out txs --- .../client/src/actions/transactions.ts | 130 +++++++++++------- .../client/src/actions/wallet.ts | 8 +- apps/simple-wallet/client/src/actions/web3.ts | 1 - .../src/components/TransactionsList.tsx | 3 +- .../client/src/containers/TransactionsList.ts | 2 +- .../client/src/reducers/transactions.ts | 8 +- 6 files changed, 94 insertions(+), 58 deletions(-) diff --git a/apps/simple-wallet/client/src/actions/transactions.ts b/apps/simple-wallet/client/src/actions/transactions.ts index 05fd37f..deca751 100644 --- a/apps/simple-wallet/client/src/actions/transactions.ts +++ b/apps/simple-wallet/client/src/actions/transactions.ts @@ -3,7 +3,7 @@ import { RootState } from '../reducers'; import { Contract } from 'web3-eth-contract'; import Web3 from 'web3'; import { TransactionReceipt } from 'web3-core'; -// import { loadBalance } from './wallet'; +import { loadWalletBalance } from './wallet'; import { loadBlock } from './blocks'; import { abi as keycardWalletABI } from '../contracts/KeycardWallet'; import { addPadding } from "../utils"; @@ -68,23 +68,6 @@ export const transactionConfirmed = (transactionHash: string): TxsTransactionCon 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(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) => { return (dispatch: Dispatch, getState: () => RootState) => { const state = getState(); @@ -93,38 +76,87 @@ export const loadTransactions = (web3: Web3, erc20: Contract) => { 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(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) => { - erc20.events.Transfer({filter: filter}).on('data', (event: any) => { - const values = event.returnValues; - dispatch(transactionDiscovered("TopUp", event.id, event.blockNumber, event.transactionHash, true, values.from, walletAddress, values.value)); - watchPendingTransaction(web3, dispatch, walletAddress, wallet, event.transactionHash); - }) + dispatch(getPastTransactions(web3, erc20, walletAddress, blockNumber, "to")) + dispatch(getPastTransactions(web3, erc20, walletAddress, blockNumber, "from")) + + dispatch(subscribeToTransactions(web3, erc20, walletAddress, blockNumber, "to")) + dispatch(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(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(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(loadWalletBalance(web3, undefined)); + } + return; + } + + window.setTimeout(() => watchPendingTransaction(web3, dispatch, walletAddress, transactionHash), 5000) + }).catch((error: string) => { + //FIXME: handle error + console.log("error", error) + }); +} diff --git a/apps/simple-wallet/client/src/actions/wallet.ts b/apps/simple-wallet/client/src/actions/wallet.ts index 6643331..0399e45 100644 --- a/apps/simple-wallet/client/src/actions/wallet.ts +++ b/apps/simple-wallet/client/src/actions/wallet.ts @@ -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) => { - 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)); + return erc20.methods.balanceOf(address).call().then((balance: string) => { dispatch(balanceLoaded(balance, balance)); return erc20; diff --git a/apps/simple-wallet/client/src/actions/web3.ts b/apps/simple-wallet/client/src/actions/web3.ts index 95e8c94..9f4cafe 100644 --- a/apps/simple-wallet/client/src/actions/web3.ts +++ b/apps/simple-wallet/client/src/actions/web3.ts @@ -12,7 +12,6 @@ export const VALID_NETWORK_ID = 3; // export const VALID_NETWORK_ID = 5; export const LOCAL_NETWORK_ID = 1337; - enum Web3Type { Generic, Remote, diff --git a/apps/simple-wallet/client/src/components/TransactionsList.tsx b/apps/simple-wallet/client/src/components/TransactionsList.tsx index 6962b28..222a9d5 100644 --- a/apps/simple-wallet/client/src/components/TransactionsList.tsx +++ b/apps/simple-wallet/client/src/components/TransactionsList.tsx @@ -39,7 +39,8 @@ const WalletsList = (props: Props) => { {props.loading &&
} - {!props.loading && + + { {props.transactions.map((tx) => ( ))} diff --git a/apps/simple-wallet/client/src/containers/TransactionsList.ts b/apps/simple-wallet/client/src/containers/TransactionsList.ts index e7cbd93..c53fbda 100644 --- a/apps/simple-wallet/client/src/containers/TransactionsList.ts +++ b/apps/simple-wallet/client/src/containers/TransactionsList.ts @@ -33,7 +33,7 @@ const mapStateToProps = (state: RootState): StateProps => { return { ...props, - loading: state.transactions.loading, + loading: state.transactions.loadingRequests > 0, transactions: transactions, } }; diff --git a/apps/simple-wallet/client/src/reducers/transactions.ts b/apps/simple-wallet/client/src/reducers/transactions.ts index ca3171b..7d5aee1 100644 --- a/apps/simple-wallet/client/src/reducers/transactions.ts +++ b/apps/simple-wallet/client/src/reducers/transactions.ts @@ -20,7 +20,7 @@ export interface TransactionState { } export interface TransactionsState { - loading: boolean + loadingRequests: number transactions: { [txHash: string]: TransactionState } @@ -39,7 +39,7 @@ const newTransactionState = (): TransactionState => ({ }); const initialState: TransactionsState = { - loading: false, + loadingRequests: 0, transactions: {}, }; @@ -48,14 +48,14 @@ export const transactionsReducer = (state: TransactionsState = initialState, act case TXS_LOADING: { return { ...state, - loading: true, + loadingRequests: state.loadingRequests + 1, } } case TXS_LOADED: { return { ...state, - loading: false, + loadingRequests: state.loadingRequests - 1, } }