-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #67 from dappforce/deploy/transaction-history
Add tx history endpoints
- Loading branch information
Showing
10 changed files
with
329 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Router } from 'express' | ||
import { getAccountTxHistory, getAccountTxHistoryWithQueue } from '../../services/txHistory' | ||
|
||
const createTxHistoryRouter = () => { | ||
const router = Router() | ||
|
||
router.get('/history/queue', async function (req, res) { | ||
const { address, pageSize, offset } = req.query | ||
const txs = await getAccountTxHistoryWithQueue({ | ||
address: address as string, | ||
pageSize: parseInt(pageSize as string), | ||
offset: parseInt(offset as string), | ||
}) | ||
|
||
res.send(txs) | ||
}) | ||
|
||
router.get('/history', async function (req, res) { | ||
const { address, pageSize, offset, networks, events } = req.query | ||
const txs = await getAccountTxHistory({ | ||
address: address as string, | ||
pageSize: parseInt(pageSize as string), | ||
offset: parseInt(offset as string), | ||
networks: networks as string[], | ||
events: events as string[], | ||
}) | ||
|
||
res.send(txs) | ||
}) | ||
|
||
return router | ||
} | ||
|
||
export default createTxHistoryRouter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { gql } from 'graphql-request' | ||
import { u8aToHex } from '@polkadot/util' | ||
import { decodeAddress, isEthereumAddress } from '@polkadot/util-crypto' | ||
import { getOrCreateQueue } from './queue' | ||
import { txAggregatorGraphQlClient } from '../../constant/graphQlClients' | ||
|
||
const ADD_QUEUE_JOB_NAME = 'REFRESH_TX_HISTORY_FOR_ACCOUNT_ON_DEMAND' | ||
|
||
const buildGetAccountTxHistoryQuery = (networks?: string[], events?: string[]) => { | ||
const networkFilterValues = networks ? ', blockchainTag: $networks' : '' | ||
const networkFilterParams = networks ? ', $networks: [BlockchainTag!]' : '' | ||
|
||
const eventFilterValues = events ? ', txKind: $txKind' : '' | ||
const eventFilterParams = events ? ', $txKind: [TransactionKind!]' : '' | ||
|
||
return gql` | ||
query getAccountTxHistory($address: String!, $pageSize: Int!, $offset: Int! ${networkFilterParams}${eventFilterParams}) { | ||
accountTxHistory( | ||
args: { where: { publicKey: $address ${networkFilterValues}${eventFilterValues} }, pageSize: $pageSize, offset: $offset } | ||
) { | ||
data { | ||
id | ||
txKind | ||
blockchainTag | ||
amount | ||
senderOrTargetPublicKey | ||
timestamp | ||
success | ||
transaction { | ||
transferNative { | ||
extrinsicHash | ||
} | ||
} | ||
} | ||
} | ||
} | ||
` | ||
} | ||
|
||
type GetAccountTransactionsWithQueue = { | ||
address: string | ||
pageSize: number | ||
offset: number | ||
} | ||
|
||
type GetAccountTransactions = GetAccountTransactionsWithQueue & { | ||
networks?: string[] | ||
events?: string[] | ||
} | ||
|
||
export const getAccountTxHistory = async ({ | ||
address, | ||
pageSize, | ||
offset, | ||
networks, | ||
events | ||
}: GetAccountTransactions) => { | ||
const networkFilterValues = networks | ||
? { networks: networks.map((network) => network.toUpperCase()) } | ||
: {} | ||
const eventsFilterValues = events ? { txKind: events.map((event) => event.toUpperCase()) } : {} | ||
|
||
const query = buildGetAccountTxHistoryQuery(networks, events) | ||
|
||
const txs = await txAggregatorGraphQlClient.request(query, { | ||
address, | ||
pageSize, | ||
offset, | ||
...networkFilterValues, | ||
...eventsFilterValues | ||
}) | ||
|
||
return txs?.accountTxHistory?.data | ||
} | ||
|
||
export const getAccountTxHistoryWithQueue = async (props: GetAccountTransactionsWithQueue) => { | ||
const txs = await getAccountTxHistory(props) | ||
|
||
const address = props.address | ||
|
||
const jobId = `${address}-${ADD_QUEUE_JOB_NAME}` | ||
const queue = getOrCreateQueue() | ||
const jobByAddress = await queue.getJob(jobId) | ||
|
||
let actualData = false | ||
|
||
if (jobByAddress) { | ||
const jobState = await jobByAddress.getState() | ||
|
||
if (jobState === 'completed') { | ||
jobByAddress.remove() | ||
|
||
actualData = true | ||
} | ||
} else { | ||
const taskPayload = { | ||
publicKey: isEthereumAddress(address) ? address : u8aToHex(decodeAddress(address)) | ||
} | ||
|
||
queue.add(ADD_QUEUE_JOB_NAME, taskPayload, { | ||
attempts: 5, | ||
jobId, | ||
removeOnComplete: false, | ||
removeOnFail: true, | ||
priority: 1 | ||
}) | ||
} | ||
|
||
return { txs, actualData } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import Queue from 'bull' | ||
|
||
let queue = null | ||
|
||
export const getOrCreateQueue = () => { | ||
if (!queue) { | ||
const host = process.env.AGGREGATOR_REDIS_HOST | ||
const password = process.env.AGGREGATOR_REDIS_PASSWORD | ||
const port = process.env.AGGREGATOR_REDIS_PORT as unknown as number | ||
|
||
const aggregatorRedisConfig = { | ||
host, | ||
password, | ||
port | ||
} | ||
|
||
queue = new Queue('ACCOUNT_AGGREGATION_FLOW', { | ||
redis: aggregatorRedisConfig, | ||
prefix: process.env.AGGREGATOR_REDIS_PREFIX, | ||
settings: { | ||
lockDuration: 20000, // Check for stalled jobs each 2 min | ||
lockRenewTime: 10000, | ||
stalledInterval: 20 * 60 * 1000, | ||
maxStalledCount: 1 | ||
} | ||
}) | ||
} | ||
|
||
return queue | ||
} |
Oops, something went wrong.