333 lines
10 KiB
TypeScript
333 lines
10 KiB
TypeScript
import { ChainId } from '@/types'
|
|
import { IndexDBStores } from '@/services/idb/@types'
|
|
import { getAllAccounts } from '@/services'
|
|
|
|
import { CHAINS, numbers, L1_CHAIN_ID } from '@/constants'
|
|
import { isEmpty, sleep, toChecksumAddress } from '@/utilities'
|
|
import { getBridgeHelper, getBridgeProxy, getAmbBridge } from '@/contracts'
|
|
|
|
import { EventsClass, GetAffirmationParams, GetRelayedMessageParams, SaveEventsParams } from './@types'
|
|
import { downloadEvents } from './load'
|
|
|
|
export * from './batch'
|
|
|
|
class EventAggregator implements EventsClass {
|
|
public async getBackupedAddressFromPublicKey(publicKey: string) {
|
|
try {
|
|
const chainId = L1_CHAIN_ID
|
|
let blockFrom = CHAINS[chainId].deployBlock
|
|
|
|
const cachedAccountsByPublicKey = await window.$nuxt.$indexedDB.getAllFromIndex({
|
|
indexName: 'key',
|
|
storeName: `${IndexDBStores.ACCOUNT_EVENTS}_${chainId}`,
|
|
key: publicKey,
|
|
})
|
|
|
|
// @ts-expect-error
|
|
const [lastAccount] = cachedAccountsByPublicKey.sort((a, b) => a.blockNumber - b.blockNumber).slice(-numbers.ONE)
|
|
|
|
if (lastAccount) {
|
|
return lastAccount.owner
|
|
}
|
|
|
|
const cachedEvents = await window.$nuxt.$indexedDB.getAll({
|
|
storeName: `${IndexDBStores.ACCOUNT_EVENTS}_${chainId}`,
|
|
})
|
|
|
|
const newEvents = []
|
|
|
|
if (cachedEvents?.length) {
|
|
const [latestEvent] = cachedEvents.slice(-numbers.ONE)
|
|
blockFrom = Number(latestEvent.blockNumber) + numbers.ONE
|
|
} else {
|
|
const downloadedEvents = await downloadEvents(`accounts_${chainId}.json`, blockFrom)
|
|
|
|
if (downloadedEvents.events.length) {
|
|
newEvents.push(...downloadedEvents.events)
|
|
|
|
blockFrom = downloadedEvents.lastBlock
|
|
}
|
|
}
|
|
|
|
const { events: graphEvents, lastSyncBlock } = await getAllAccounts({ fromBlock: blockFrom, chainId })
|
|
|
|
newEvents.push(...graphEvents)
|
|
|
|
const [account] = newEvents.filter((e: { key: string }) => e.key === publicKey)
|
|
|
|
if (account) {
|
|
this.saveEvents({
|
|
chainId,
|
|
events: newEvents,
|
|
storeName: IndexDBStores.ACCOUNT_EVENTS,
|
|
})
|
|
return account.owner
|
|
}
|
|
|
|
if (lastSyncBlock) {
|
|
blockFrom = lastSyncBlock
|
|
}
|
|
|
|
const bridgeHelper = getBridgeHelper(chainId)
|
|
|
|
const filter = bridgeHelper.filters.PublicKey()
|
|
const rpcEvents = await bridgeHelper.queryFilter(filter, blockFrom)
|
|
|
|
const accountEvents = rpcEvents.map((e: { args: { key: string; owner: string }; blockNumber: number }) => {
|
|
return {
|
|
key: e.args.key,
|
|
owner: toChecksumAddress(e.args.owner),
|
|
blockNumber: e.blockNumber,
|
|
}
|
|
})
|
|
|
|
newEvents.push(...accountEvents)
|
|
|
|
this.saveEvents({
|
|
chainId,
|
|
events: newEvents,
|
|
storeName: IndexDBStores.ACCOUNT_EVENTS,
|
|
})
|
|
|
|
const events = newEvents.filter((e: { key: string }) => e.key === publicKey)
|
|
|
|
if (isEmpty(events)) {
|
|
return undefined
|
|
}
|
|
|
|
// @ts-expect-error
|
|
const [event] = events.sort((a, b) => a.blockNumber - b.blockNumber).slice(-numbers.ONE)
|
|
|
|
return event.owner
|
|
} catch (err) {
|
|
console.log(err)
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
public async getAccountAddress(address: string) {
|
|
try {
|
|
const chainId = L1_CHAIN_ID
|
|
let blockFrom = CHAINS[chainId].deployBlock
|
|
|
|
const cachedAccountsByOwner = await window.$nuxt.$indexedDB.getAllFromIndex({
|
|
indexName: 'owner',
|
|
storeName: `${IndexDBStores.ACCOUNT_EVENTS}_${chainId}`,
|
|
key: toChecksumAddress(address),
|
|
})
|
|
|
|
// @ts-expect-error
|
|
const [lastAccount] = cachedAccountsByOwner.sort((a, b) => a.blockNumber - b.blockNumber).slice(-numbers.ONE)
|
|
|
|
if (lastAccount) {
|
|
return lastAccount.key
|
|
}
|
|
|
|
const cachedEvents = await window.$nuxt.$indexedDB.getAll({
|
|
storeName: `${IndexDBStores.ACCOUNT_EVENTS}_${chainId}`,
|
|
})
|
|
|
|
const newEvents = []
|
|
|
|
if (cachedEvents?.length) {
|
|
const [latestEvent] = cachedEvents.slice(-numbers.ONE)
|
|
blockFrom = Number(latestEvent.blockNumber) + numbers.ONE
|
|
} else {
|
|
const downloadedEvents = await downloadEvents(`accounts_${chainId}.json`, blockFrom)
|
|
|
|
if (downloadedEvents.events.length) {
|
|
newEvents.push(...downloadedEvents.events)
|
|
|
|
blockFrom = downloadedEvents.lastBlock
|
|
}
|
|
}
|
|
|
|
const { events: graphEvents, lastSyncBlock } = await getAllAccounts({ fromBlock: blockFrom, chainId })
|
|
newEvents.push(...graphEvents)
|
|
|
|
const [account] = newEvents.filter((e: { owner: string }) => toChecksumAddress(e.owner) === toChecksumAddress(address))
|
|
|
|
if (account) {
|
|
this.saveEvents({
|
|
chainId,
|
|
events: newEvents,
|
|
storeName: IndexDBStores.ACCOUNT_EVENTS,
|
|
})
|
|
return account.key
|
|
}
|
|
|
|
if (lastSyncBlock) {
|
|
blockFrom = lastSyncBlock
|
|
}
|
|
|
|
const bridgeHelper = getBridgeHelper(chainId)
|
|
|
|
const filter = bridgeHelper.filters.PublicKey()
|
|
const rpcEvents = await bridgeHelper.queryFilter(filter, blockFrom)
|
|
|
|
const accountEvents = rpcEvents.map((e: { args: { key: string; owner: string }; blockNumber: number }) => {
|
|
return {
|
|
key: e.args.key,
|
|
owner: toChecksumAddress(e.args.owner),
|
|
blockNumber: e.blockNumber,
|
|
}
|
|
})
|
|
|
|
newEvents.push(...accountEvents)
|
|
|
|
this.saveEvents({
|
|
chainId,
|
|
events: newEvents,
|
|
storeName: IndexDBStores.ACCOUNT_EVENTS,
|
|
})
|
|
|
|
const events = cachedEvents
|
|
.concat(newEvents)
|
|
.filter((e: { owner: string }) => toChecksumAddress(e.owner) === toChecksumAddress(address))
|
|
|
|
if (isEmpty(events)) {
|
|
return undefined
|
|
}
|
|
|
|
// @ts-expect-error
|
|
const [event] = events.sort((a, b) => a.blockNumber - b.blockNumber).slice(-numbers.ONE)
|
|
|
|
return event.key
|
|
} catch (err) {
|
|
console.log(err)
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
// @ts-expect-error
|
|
public async getRelayedMessage({ messageId, blockFrom = numbers.ZERO, attempt = numbers.ONE }: GetRelayedMessageParams) {
|
|
try {
|
|
const ambBridge = getBridgeProxy(L1_CHAIN_ID)
|
|
const callRetryAttempt = 150
|
|
|
|
if (!blockFrom) {
|
|
// TODO save block number after start processing transaction
|
|
blockFrom = await ambBridge.provider.getBlockNumber()
|
|
blockFrom = blockFrom - Number('100')
|
|
}
|
|
|
|
const filter = ambBridge.filters.RelayedMessage(null, null, messageId)
|
|
const events = await ambBridge.queryFilter(filter, blockFrom)
|
|
|
|
if (!isEmpty(events)) {
|
|
return events
|
|
} else if (attempt >= callRetryAttempt) {
|
|
return []
|
|
} else {
|
|
attempt++
|
|
await sleep(numbers.SECOND * numbers.THREE)
|
|
return this.getRelayedMessage({ messageId, blockFrom, attempt })
|
|
}
|
|
} catch (err) {
|
|
attempt++
|
|
await sleep(numbers.SECOND * numbers.TEN)
|
|
return this.getRelayedMessage({ messageId, blockFrom, attempt })
|
|
}
|
|
}
|
|
|
|
// @ts-expect-error
|
|
public async getAffirmationCompleted({ messageId, blockFrom = numbers.ZERO, attempt = numbers.ONE }: GetAffirmationParams) {
|
|
try {
|
|
const ambBridge = getAmbBridge(ChainId.XDAI)
|
|
const callRetryAttempt = 150
|
|
|
|
if (!blockFrom) {
|
|
// TODO save block number after start processing transaction
|
|
blockFrom = await ambBridge.provider.getBlockNumber()
|
|
blockFrom = blockFrom - Number('100')
|
|
}
|
|
|
|
const filter = ambBridge.filters.AffirmationCompleted(null, null, messageId)
|
|
const events = await ambBridge.queryFilter(filter, blockFrom)
|
|
|
|
if (!isEmpty(events)) {
|
|
return events
|
|
} else if (attempt >= callRetryAttempt) {
|
|
return []
|
|
} else {
|
|
attempt++
|
|
await sleep(numbers.SECOND * numbers.THREE)
|
|
return this.getAffirmationCompleted({ messageId, attempt })
|
|
}
|
|
} catch (err) {
|
|
attempt++
|
|
await sleep(numbers.SECOND * numbers.TEN)
|
|
return this.getAffirmationCompleted({ messageId, attempt })
|
|
}
|
|
}
|
|
|
|
// @ts-expect-error
|
|
public async getUserRequestForSignature({ blockFrom, messageId, attempt = numbers.ONE }: GetAffirmationParams) {
|
|
try {
|
|
const ambBridge = getAmbBridge(ChainId.XDAI)
|
|
const callRetryAttempt = 150
|
|
|
|
const filter = ambBridge.filters.UserRequestForSignature(messageId)
|
|
const events = await ambBridge.queryFilter(filter, blockFrom)
|
|
|
|
if (!isEmpty(events)) {
|
|
return events
|
|
} else if (attempt >= callRetryAttempt) {
|
|
return []
|
|
} else {
|
|
attempt++
|
|
await sleep(numbers.SECOND * numbers.THREE)
|
|
return this.getUserRequestForSignature({ blockFrom, attempt })
|
|
}
|
|
} catch (err) {
|
|
attempt++
|
|
await sleep(numbers.SECOND * numbers.TEN)
|
|
return this.getUserRequestForSignature({ blockFrom, attempt })
|
|
}
|
|
}
|
|
|
|
// @ts-expect-error
|
|
public async getUserRequestForAffirmation({ messageId, blockFrom, attempt = numbers.ONE }: GetAffirmationParams) {
|
|
try {
|
|
const bridge = getBridgeProxy(L1_CHAIN_ID)
|
|
const callRetryAttempt = 150
|
|
|
|
const filter = bridge.filters.UserRequestForAffirmation(messageId)
|
|
const events = await bridge.queryFilter(filter, blockFrom)
|
|
|
|
if (!isEmpty(events)) {
|
|
return events
|
|
} else if (attempt >= callRetryAttempt) {
|
|
return []
|
|
} else {
|
|
attempt++
|
|
await sleep(numbers.SECOND * numbers.THREE)
|
|
return this.getUserRequestForAffirmation({ blockFrom, attempt })
|
|
}
|
|
} catch (err) {
|
|
attempt++
|
|
await sleep(numbers.SECOND * numbers.TEN)
|
|
return this.getUserRequestForAffirmation({ blockFrom, attempt })
|
|
}
|
|
}
|
|
|
|
saveEvents({ events, storeName, chainId }: SaveEventsParams) {
|
|
try {
|
|
if (window.$nuxt.$indexedDB.isBlocked || isEmpty(events)) {
|
|
return
|
|
}
|
|
|
|
window.$nuxt.$indexedDB.createMultipleTransactions({
|
|
data: events,
|
|
storeName: `${storeName}_${chainId}`,
|
|
})
|
|
} catch (err) {
|
|
console.error(`saveEvents has error: ${err.message}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
const eventService = new EventAggregator()
|
|
|
|
export { eventService }
|