Skip to content

Commit

Permalink
Merging events into txs (#49)
Browse files Browse the repository at this point in the history
* Function to classify an event as a inconming trasaction or not

* getTransaction function

* Implementing get transaction function to get the data from the transaction contained in the event object

* Adding tests

* Adding incoming txs

* Fixing tests

* Adding refactoring to merging

Co-authored-by: Agustin Villalobos <[email protected]>
  • Loading branch information
sleyter93 and Agustin Villalobos authored Aug 12, 2022
1 parent 4f07cfe commit 9a419d2
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/repository/DataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export abstract class DataSource {
abstract getTokensByAddress(address: string);
abstract getRbtcBalanceByAddress(address: string);
abstract getEventsByAddress(address: string);
abstract getTransaction(hash: string);
abstract getTransactionsByAddress(address:string,
limit?: string,
prev?: string,
Expand Down
16 changes: 14 additions & 2 deletions src/rskExplorerApi/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@

import _axios from 'axios'
import { DataSource } from '../repository/DataSource'
import {
EventsServerResponse,
TransactionsServerResponse,
TokensServerResponse,
IApiRbtcBalance,
RbtcBalancesServerResponse
RbtcBalancesServerResponse,
TransactionServerResponse
} from './types'
import { fromApiToRtbcBalance, fromApiToTEvents, fromApiToTokens, fromApiToTokenWithBalance } from './utils'

Expand Down Expand Up @@ -70,6 +70,18 @@ export class RSKExplorerAPI extends DataSource {
return [fromApiToRtbcBalance(balanceInLatestBlock, this.chainId)]
}

async getTransaction (hash: string) {
const params = {
module: 'transactions',
action: 'getTransaction',
hash
}

const response = await this.axios!.get<TransactionServerResponse>(this.url, { params })

return response.data.data
}

async getTransactionsByAddress (
address:string,
limit?: string | undefined,
Expand Down
4 changes: 4 additions & 0 deletions src/rskExplorerApi/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,7 @@ export interface ChannelServerResponse {
action: string;
data: TransactionsServerResponse
}

export interface TransactionServerResponse {
data: IApiTransactions
}
22 changes: 21 additions & 1 deletion src/service/transaction/transactionProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DataSource } from '../../repository/DataSource'
import type { Event } from '../../types/event'
import { PollingProvider } from '../AbstractPollingProvider'
import { isIncomingTransaction } from './utils'

export class TransactionProvider extends PollingProvider<Event> {
private dataSource: DataSource
Expand All @@ -10,13 +11,32 @@ export class TransactionProvider extends PollingProvider<Event> {
this.dataSource = dataSource
}

async getIncomingTransactions (address: string) {
const events = await this.dataSource.getEventsByAddress(this.address.toLowerCase())
.then(events => events.filter(event => isIncomingTransaction(event, address)))
.catch(() => [])

const txs = events
.map(event => this.dataSource.getTransaction(event.transactionHash))

const result = await Promise.all(txs)
return result
}

async getTransactionsPaginated (address: string, limit?: string, prev?: string, next?: string) {
return this.dataSource.getTransactionsByAddress(address, limit, prev, next)
}

async poll () {
const txs = await this.getIncomingTransactions(this.address)
.then(transactions => transactions)
.catch(() => [])

const events = await this.getTransactionsPaginated(this.address)
.then(transactions => transactions.data.map(transaction => ({ type: 'newTransaction', payload: transaction })))
.then(transactions => [...transactions.data, ...txs]
.sort((txA, txB) => txA.timestamp - txB.timestamp)
.map(transaction => ({ type: 'newTransaction', payload: transaction }))
)
.catch(() => [])
return events
}
Expand Down
5 changes: 5 additions & 0 deletions src/service/transaction/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IEvent } from '../../rskExplorerApi/types'

export function isIncomingTransaction (event: IEvent, address: string) {
return event.args[1].toLowerCase() === address.toLowerCase()
}
38 changes: 38 additions & 0 deletions test/mockAddressResponses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,44 @@ export const transactionResponse = {
]
}

export const txResponse = {
data:
{
_id: '61ef707ae13e1554d70cd1b8',
hash: '0x82dbb412363693d7334d1e3fc15b7a6453861ffa16d511966565443ff73699dd',
nonce: 30,
blockHash: '0x5ddd52bde4249f382b3c95da3443425e5671c8ae86416ae1a107721eb830b92a',
blockNumber: 2526431,
transactionIndex: 0,
from: '0xdf3f858032e370ae039417b278403ba90ab8bb64',
to: '0xef69146474ee7fff43f78b2860518b005afae9f8',
gas: 35948,
gasPrice: '0x3ec4458',
value: '0x11c37937e08000',
input: '0x244f53b5000000000000000000000000',
v: '0x61',
r: '0x765df5df534909d41cd4fef4b783c653b9d5c3bf92a8227408c970ad19af9c31',
s: '0x462f9b61cbc808fb490c8353e8e93c23c764417ff66ff794694d8ee14dadb03a',
timestamp: 1643081782,
receipt: {
transactionHash: '0x82dbb412363693d7334d1e3fc15b7a6453861ffa16d511966565443ff73699dd',
transactionIndex: 0,
blockHash: '0x5ddd52bde4249f382b3c95da3443425e5671c8ae86416ae1a107721eb830b92a',
blockNumber: 2526431,
cumulativeGasUsed: 35948,
gasUsed: 35948,
contractAddress: null,
logs: [],
from: '0xdf3f858032e370ae039417b278403ba90ab8bb64',
to: '0xef69146474ee7fff43f78b2860518b005afae9f8',
status: '0x0',
logsBloom: '0x000000000000000000000000000000000000000000000'
},
txType: 'contract call',
txId: '0268cdf000ae1a107721eb830b92a'
}
}

export const eventResponse = [{
blockNumber: 2525443,
event: 'Transfer',
Expand Down
10 changes: 6 additions & 4 deletions test/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BalanceProvider } from '../src/service/balance/balanceProvider'
import { LastPrice } from '../src/service/price/lastPrice'
import { PriceCollector } from '../src/service/price/priceCollector'
import { TransactionProvider } from '../src/service/transaction/transactionProvider'
import { mockAddress, tokenResponse, transactionResponse, eventResponse } from './mockAddressResponses'
import { mockAddress, tokenResponse, transactionResponse, eventResponse, txResponse } from './mockAddressResponses'
import { pricesResponse } from './mockPriceResponses'
import { TokenTransferProvider } from '../src/service/tokenTransfer/tokenTransferProvider'
import { MockPrice } from '../src/service/price/mockPrice'
Expand All @@ -11,17 +11,19 @@ const getTransactionsByAddressMock = jest.fn(() => Promise.resolve((transactionR
const getQuotesLatestMock = jest.fn(() => Promise.resolve(pricesResponse))
const getTokensByAddressMock = jest.fn(() => Promise.resolve(tokenResponse))
const getEventsByAddressMock = jest.fn(() => Promise.resolve(eventResponse))
const getTransactionMock = jest.fn(() => Promise.resolve(txResponse))

describe('Emmitting Events', () => {
test('emit transactions', async () => {
const rskExplorerApiMock = {
getTransactionsByAddress: getTransactionsByAddressMock
getTransactionsByAddress: getTransactionsByAddressMock,
getTransaction: getTransactionMock,
getEventsByAddress: getEventsByAddressMock
}
const transactionProvider = new TransactionProvider(mockAddress, rskExplorerApiMock as any)
transactionProvider.on(mockAddress, async (data) => {
const { type, payload } = data
const { type } = data
expect(type).toEqual('newTransaction')
expect(payload).toEqual(transactionResponse.data[0])
})
await transactionProvider.subscribe(mockAddress)
transactionProvider.unsubscribe()
Expand Down

0 comments on commit 9a419d2

Please sign in to comment.