Fallback eth_getLogs to explorer API for BSC (#530)
This commit is contained in:
parent
f93ab330cc
commit
818bc4675d
@ -19,7 +19,7 @@ COPY --from=contracts /mono/contracts/build ./contracts/build
|
||||
COPY commons/package.json ./commons/
|
||||
COPY alm/package.json ./alm/
|
||||
COPY yarn.lock .
|
||||
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production
|
||||
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile
|
||||
|
||||
COPY ./commons ./commons
|
||||
COPY ./alm ./alm
|
||||
|
@ -58,6 +58,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-plugin-prettier": "^3.1.3"
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"node-fetch": "^2.6.1"
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ const { BRIDGE_VALIDATORS_ABI, HOME_AMB_ABI } = require('commons')
|
||||
const path = require('path')
|
||||
require('dotenv').config()
|
||||
const Web3 = require('web3')
|
||||
const fetch = require('node-fetch')
|
||||
const { URL } = require('url')
|
||||
|
||||
const fs = require('fs')
|
||||
|
||||
@ -10,7 +12,9 @@ const {
|
||||
COMMON_HOME_RPC_URL,
|
||||
COMMON_HOME_BRIDGE_ADDRESS,
|
||||
COMMON_FOREIGN_RPC_URL,
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||
ALM_FOREIGN_EXPLORER_API,
|
||||
ALM_HOME_EXPLORER_API
|
||||
} = process.env
|
||||
|
||||
const generateSnapshot = async (side, url, bridgeAddress) => {
|
||||
@ -19,6 +23,31 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
||||
const snapshot = {}
|
||||
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(url))
|
||||
const api = side === 'home' ? ALM_HOME_EXPLORER_API : ALM_FOREIGN_EXPLORER_API
|
||||
|
||||
const getPastEventsWithFallback = (contract, eventName, options) =>
|
||||
contract.getPastEvents(eventName, options).catch(async e => {
|
||||
if (e.message.includes('exceed maximum block range')) {
|
||||
const abi = contract.options.jsonInterface.find(abi => abi.type === 'event' && abi.name === eventName)
|
||||
|
||||
const url = new URL(api)
|
||||
url.searchParams.append('module', 'logs')
|
||||
url.searchParams.append('action', 'getLogs')
|
||||
url.searchParams.append('address', contract.options.address)
|
||||
url.searchParams.append('fromBlock', options.fromBlock)
|
||||
url.searchParams.append('toBlock', options.toBlock || 'latest')
|
||||
url.searchParams.append('topic0', web3.eth.abi.encodeEventSignature(abi))
|
||||
|
||||
const logs = await fetch(url).then(res => res.json())
|
||||
|
||||
return logs.result.map(log => ({
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: parseInt(log.blockNumber.slice(2), 16),
|
||||
returnValues: web3.eth.abi.decodeLog(abi.inputs, log.data, log.topics.slice(1))
|
||||
}))
|
||||
}
|
||||
throw e
|
||||
})
|
||||
|
||||
const currentBlockNumber = await web3.eth.getBlockNumber()
|
||||
snapshot.snapshotBlockNumber = currentBlockNumber
|
||||
@ -29,10 +58,14 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
||||
const bridgeContract = new web3.eth.Contract(HOME_AMB_ABI, bridgeAddress)
|
||||
|
||||
// Save RequiredBlockConfirmationChanged events
|
||||
let requiredBlockConfirmationChangedEvents = await bridgeContract.getPastEvents('RequiredBlockConfirmationChanged', {
|
||||
fromBlock: 0,
|
||||
toBlock: currentBlockNumber
|
||||
})
|
||||
let requiredBlockConfirmationChangedEvents = await getPastEventsWithFallback(
|
||||
bridgeContract,
|
||||
'RequiredBlockConfirmationChanged',
|
||||
{
|
||||
fromBlock: 0,
|
||||
toBlock: currentBlockNumber
|
||||
}
|
||||
)
|
||||
|
||||
// In case RequiredBlockConfirmationChanged was not emitted during initialization in early versions of AMB
|
||||
// manually generate an event for this. Example Sokol - Kovan bridge
|
||||
@ -59,10 +92,14 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
||||
const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorAddress)
|
||||
|
||||
// Save RequiredSignaturesChanged events
|
||||
const RequiredSignaturesChangedEvents = await validatorContract.getPastEvents('RequiredSignaturesChanged', {
|
||||
fromBlock: 0,
|
||||
toBlock: currentBlockNumber
|
||||
})
|
||||
const RequiredSignaturesChangedEvents = await getPastEventsWithFallback(
|
||||
validatorContract,
|
||||
'RequiredSignaturesChanged',
|
||||
{
|
||||
fromBlock: 0,
|
||||
toBlock: currentBlockNumber
|
||||
}
|
||||
)
|
||||
snapshot.RequiredSignaturesChanged = RequiredSignaturesChangedEvents.map(e => ({
|
||||
blockNumber: e.blockNumber,
|
||||
returnValues: {
|
||||
@ -71,7 +108,7 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
||||
}))
|
||||
|
||||
// Save ValidatorAdded events
|
||||
const validatorAddedEvents = await validatorContract.getPastEvents('ValidatorAdded', {
|
||||
const validatorAddedEvents = await getPastEventsWithFallback(validatorContract, 'ValidatorAdded', {
|
||||
fromBlock: 0,
|
||||
toBlock: currentBlockNumber
|
||||
})
|
||||
@ -85,7 +122,7 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
||||
}))
|
||||
|
||||
// Save ValidatorRemoved events
|
||||
const validatorRemovedEvents = await validatorContract.getPastEvents('ValidatorRemoved', {
|
||||
const validatorRemovedEvents = await getPastEventsWithFallback(validatorContract, 'ValidatorRemoved', {
|
||||
fromBlock: 0,
|
||||
toBlock: currentBlockNumber
|
||||
})
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
DOUBLE_EXECUTION_ATTEMPT_ERROR,
|
||||
EXECUTION_FAILED_ERROR,
|
||||
EXECUTION_OUT_OF_GAS_ERROR,
|
||||
FOREIGN_EXPLORER_API,
|
||||
INCORRECT_CHAIN_ERROR,
|
||||
VALIDATOR_CONFIRMATION_STATUS
|
||||
} from '../config/constants'
|
||||
@ -92,7 +93,13 @@ export const ManualExecutionButton = ({
|
||||
})
|
||||
.on('error', async (e: Error, receipt: TransactionReceipt) => {
|
||||
if (e.message.includes('Transaction has been reverted by the EVM')) {
|
||||
const successExecutionData = await getSuccessExecutionData(bridge, 'RelayedMessage', library, messageId)
|
||||
const successExecutionData = await getSuccessExecutionData(
|
||||
bridge,
|
||||
'RelayedMessage',
|
||||
library,
|
||||
messageId,
|
||||
FOREIGN_EXPLORER_API
|
||||
)
|
||||
if (successExecutionData) {
|
||||
setExecutionData(successExecutionData)
|
||||
setError(DOUBLE_EXECUTION_ATTEMPT_ERROR)
|
||||
|
@ -4,6 +4,8 @@ import { useStateProvider } from '../state/StateProvider'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
import { getRequiredBlockConfirmations } from '../utils/contract'
|
||||
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
|
||||
import Web3 from 'web3'
|
||||
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
|
||||
|
||||
export interface UseBlockConfirmationsParams {
|
||||
fromHome: boolean
|
||||
@ -19,9 +21,11 @@ export const useBlockConfirmations = ({ receipt, fromHome }: UseBlockConfirmatio
|
||||
contract: Contract,
|
||||
receipt: TransactionReceipt,
|
||||
setResult: Function,
|
||||
snapshotProvider: SnapshotProvider
|
||||
snapshotProvider: SnapshotProvider,
|
||||
web3: Web3,
|
||||
api: string
|
||||
) => {
|
||||
const result = await getRequiredBlockConfirmations(contract, receipt.blockNumber, snapshotProvider)
|
||||
const result = await getRequiredBlockConfirmations(contract, receipt.blockNumber, snapshotProvider, web3, api)
|
||||
setResult(result)
|
||||
}
|
||||
|
||||
@ -29,10 +33,12 @@ export const useBlockConfirmations = ({ receipt, fromHome }: UseBlockConfirmatio
|
||||
() => {
|
||||
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
||||
const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider
|
||||
if (!bridgeContract || !receipt) return
|
||||
callRequireBlockConfirmations(bridgeContract, receipt, setBlockConfirmations, snapshotProvider)
|
||||
const web3 = fromHome ? home.web3 : foreign.web3
|
||||
const api = fromHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API
|
||||
if (!bridgeContract || !receipt || !web3) return
|
||||
callRequireBlockConfirmations(bridgeContract, receipt, setBlockConfirmations, snapshotProvider, web3, api)
|
||||
},
|
||||
[home.bridgeContract, foreign.bridgeContract, receipt, fromHome]
|
||||
[home.bridgeContract, foreign.bridgeContract, receipt, fromHome, home.web3, foreign.web3]
|
||||
)
|
||||
|
||||
return {
|
||||
|
@ -6,6 +6,7 @@ import { BRIDGE_VALIDATORS_ABI } from '../abis'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
|
||||
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
|
||||
|
||||
export interface useValidatorContractParams {
|
||||
fromHome: boolean
|
||||
@ -30,10 +31,12 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
|
||||
contract: Maybe<Contract>,
|
||||
receipt: TransactionReceipt,
|
||||
setResult: Function,
|
||||
snapshotProvider: SnapshotProvider
|
||||
snapshotProvider: SnapshotProvider,
|
||||
web3: Web3,
|
||||
api: string
|
||||
) => {
|
||||
if (!contract) return
|
||||
const result = await getRequiredSignatures(contract, receipt.blockNumber, snapshotProvider)
|
||||
const result = await getRequiredSignatures(contract, receipt.blockNumber, snapshotProvider, web3, api)
|
||||
setResult(result)
|
||||
}
|
||||
|
||||
@ -41,32 +44,35 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
|
||||
contract: Maybe<Contract>,
|
||||
receipt: TransactionReceipt,
|
||||
setResult: Function,
|
||||
snapshotProvider: SnapshotProvider
|
||||
snapshotProvider: SnapshotProvider,
|
||||
web3: Web3,
|
||||
api: string
|
||||
) => {
|
||||
if (!contract) return
|
||||
const result = await getValidatorList(contract, receipt.blockNumber, snapshotProvider)
|
||||
const result = await getValidatorList(contract, receipt.blockNumber, snapshotProvider, web3, api)
|
||||
setResult(result)
|
||||
}
|
||||
|
||||
const web3 = fromHome ? home.web3 : foreign.web3
|
||||
const api = fromHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API
|
||||
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
||||
const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const web3 = fromHome ? home.web3 : foreign.web3
|
||||
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
||||
|
||||
if (!web3 || !bridgeContract) return
|
||||
callValidatorContract(bridgeContract, web3, setValidatorContract)
|
||||
},
|
||||
[home.web3, foreign.web3, home.bridgeContract, foreign.bridgeContract, fromHome]
|
||||
[web3, bridgeContract]
|
||||
)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!receipt) return
|
||||
const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider
|
||||
callRequiredSignatures(validatorContract, receipt, setRequiredSignatures, snapshotProvider)
|
||||
callValidatorList(validatorContract, receipt, setValidatorList, snapshotProvider)
|
||||
if (!web3 || !receipt) return
|
||||
callRequiredSignatures(validatorContract, receipt, setRequiredSignatures, snapshotProvider, web3, api)
|
||||
callValidatorList(validatorContract, receipt, setValidatorList, snapshotProvider, web3, api)
|
||||
},
|
||||
[validatorContract, receipt, fromHome]
|
||||
[validatorContract, receipt, web3, snapshotProvider, api]
|
||||
)
|
||||
|
||||
return {
|
||||
|
@ -16,7 +16,7 @@ describe('getRequiredBlockConfirmations', () => {
|
||||
|
||||
test('Should call requiredBlockConfirmations method if no events present', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: () => {
|
||||
getPastEvents: async () => {
|
||||
return []
|
||||
},
|
||||
methods: methodsBuilder('1')
|
||||
@ -37,7 +37,7 @@ describe('getRequiredBlockConfirmations', () => {
|
||||
})
|
||||
test('Should not call to get events if block number was included in the snapshot', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => []),
|
||||
getPastEvents: jest.fn().mockImplementation(async () => []),
|
||||
methods: methodsBuilder('3')
|
||||
} as unknown) as Contract
|
||||
|
||||
@ -64,7 +64,7 @@ describe('getRequiredBlockConfirmations', () => {
|
||||
})
|
||||
test('Should call to get events if block number was not included in the snapshot', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => [
|
||||
getPastEvents: jest.fn().mockImplementation(async () => [
|
||||
{
|
||||
blockNumber: 9,
|
||||
returnValues: {
|
||||
@ -102,7 +102,7 @@ describe('getRequiredBlockConfirmations', () => {
|
||||
})
|
||||
test('Should use the most updated event', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => [
|
||||
getPastEvents: jest.fn().mockImplementation(async () => [
|
||||
{
|
||||
blockNumber: 9,
|
||||
returnValues: {
|
||||
@ -141,7 +141,7 @@ describe('getRequiredBlockConfirmations', () => {
|
||||
describe('getRequiredSignatures', () => {
|
||||
test('Should not call to get events if block number was included in the snapshot', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => [])
|
||||
getPastEvents: jest.fn().mockImplementation(async () => [])
|
||||
} as unknown) as Contract
|
||||
|
||||
const snapshotProvider = ({
|
||||
@ -173,7 +173,7 @@ describe('getRequiredSignatures', () => {
|
||||
})
|
||||
test('Should call to get events if block number is higher than the snapshot block number', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => [
|
||||
getPastEvents: jest.fn().mockImplementation(async () => [
|
||||
{
|
||||
blockNumber: 15,
|
||||
returnValues: {
|
||||
@ -216,7 +216,7 @@ describe('getRequiredSignatures', () => {
|
||||
})
|
||||
test('Should use the most updated event before the block number', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => [
|
||||
getPastEvents: jest.fn().mockImplementation(async () => [
|
||||
{
|
||||
blockNumber: 15,
|
||||
returnValues: {
|
||||
@ -270,7 +270,7 @@ describe('getValidatorList', () => {
|
||||
test('Should return the current validator list if no events found', async () => {
|
||||
const currentValidators = [validator1, validator2, validator3]
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => []),
|
||||
getPastEvents: jest.fn().mockImplementation(async () => []),
|
||||
methods: methodsBuilder(currentValidators)
|
||||
} as unknown) as Contract
|
||||
|
||||
@ -301,7 +301,7 @@ describe('getValidatorList', () => {
|
||||
test('If validator was added later from snapshot it should not include it', async () => {
|
||||
const currentValidators = [validator1, validator2, validator3]
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => []),
|
||||
getPastEvents: jest.fn().mockImplementation(async () => []),
|
||||
methods: methodsBuilder(currentValidators)
|
||||
} as unknown) as Contract
|
||||
|
||||
@ -340,7 +340,7 @@ describe('getValidatorList', () => {
|
||||
test('If validator was added later from chain it should not include it', async () => {
|
||||
const currentValidators = [validator1, validator2, validator3]
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(event => {
|
||||
getPastEvents: jest.fn().mockImplementation(async event => {
|
||||
if (event === 'ValidatorAdded') {
|
||||
return [
|
||||
{
|
||||
@ -385,7 +385,7 @@ describe('getValidatorList', () => {
|
||||
test('If validator was removed later from snapshot it should include it', async () => {
|
||||
const currentValidators = [validator1, validator2]
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(() => []),
|
||||
getPastEvents: jest.fn().mockImplementation(async () => []),
|
||||
methods: methodsBuilder(currentValidators)
|
||||
} as unknown) as Contract
|
||||
|
||||
@ -424,7 +424,7 @@ describe('getValidatorList', () => {
|
||||
test('If validator was removed later from chain it should include it', async () => {
|
||||
const currentValidators = [validator1, validator2]
|
||||
const contract = ({
|
||||
getPastEvents: jest.fn().mockImplementation(event => {
|
||||
getPastEvents: jest.fn().mockImplementation(async event => {
|
||||
if (event === 'ValidatorRemoved') {
|
||||
return [
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ beforeEach(() => {
|
||||
describe('getFinalizationEvent', () => {
|
||||
test('should get finalization event and not try to get failed or pending transactions', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: () => {
|
||||
getPastEvents: async () => {
|
||||
return [event]
|
||||
}
|
||||
} as unknown) as Contract
|
||||
@ -102,7 +102,7 @@ describe('getFinalizationEvent', () => {
|
||||
})
|
||||
test('should retry to get finalization event and not try to get failed or pending transactions if foreign to home transaction', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: () => {
|
||||
getPastEvents: async () => {
|
||||
return []
|
||||
}
|
||||
} as unknown) as Contract
|
||||
@ -147,7 +147,7 @@ describe('getFinalizationEvent', () => {
|
||||
})
|
||||
test('should retry to get finalization event and try to get failed and pending transactions if home to foreign transaction', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: () => {
|
||||
getPastEvents: async () => {
|
||||
return []
|
||||
},
|
||||
options: {
|
||||
@ -199,7 +199,7 @@ describe('getFinalizationEvent', () => {
|
||||
})
|
||||
test('should retry to get finalization event and not to try to get failed transaction if pending transactions found if home to foreign transaction', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: () => {
|
||||
getPastEvents: async () => {
|
||||
return []
|
||||
},
|
||||
options: {
|
||||
@ -258,7 +258,7 @@ describe('getFinalizationEvent', () => {
|
||||
})
|
||||
test('should retry to get finalization event even if failed transaction found if home to foreign transaction', async () => {
|
||||
const contract = ({
|
||||
getPastEvents: () => {
|
||||
getPastEvents: async () => {
|
||||
return []
|
||||
},
|
||||
options: {
|
||||
|
@ -1,18 +1,33 @@
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
import { EventData } from 'web3-eth-contract'
|
||||
import { SnapshotProvider } from '../services/SnapshotProvider'
|
||||
import { getLogs } from './explorer'
|
||||
import Web3 from 'web3'
|
||||
|
||||
const getPastEventsWithFallback = (
|
||||
api: string,
|
||||
web3: Web3 | null,
|
||||
contract: Contract,
|
||||
eventName: string,
|
||||
options: any
|
||||
) =>
|
||||
contract
|
||||
.getPastEvents(eventName, options)
|
||||
.catch(() => (api && web3 ? getLogs(api, web3, contract, eventName, options) : []))
|
||||
|
||||
export const getRequiredBlockConfirmations = async (
|
||||
contract: Contract,
|
||||
blockNumber: number,
|
||||
snapshotProvider: SnapshotProvider
|
||||
snapshotProvider: SnapshotProvider,
|
||||
web3: Web3 | null = null,
|
||||
api: string = ''
|
||||
) => {
|
||||
const eventsFromSnapshot = snapshotProvider.requiredBlockConfirmationEvents(blockNumber)
|
||||
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
||||
|
||||
let contractEvents: EventData[] = []
|
||||
if (blockNumber > snapshotBlockNumber) {
|
||||
contractEvents = await contract.getPastEvents('RequiredBlockConfirmationChanged', {
|
||||
contractEvents = await getPastEventsWithFallback(api, web3, contract, 'RequiredBlockConfirmationChanged', {
|
||||
fromBlock: snapshotBlockNumber + 1,
|
||||
toBlock: blockNumber
|
||||
})
|
||||
@ -38,14 +53,16 @@ export const getValidatorAddress = (contract: Contract) => contract.methods.vali
|
||||
export const getRequiredSignatures = async (
|
||||
contract: Contract,
|
||||
blockNumber: number,
|
||||
snapshotProvider: SnapshotProvider
|
||||
snapshotProvider: SnapshotProvider,
|
||||
web3: Web3 | null = null,
|
||||
api: string = ''
|
||||
) => {
|
||||
const eventsFromSnapshot = snapshotProvider.requiredSignaturesEvents(blockNumber)
|
||||
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
||||
|
||||
let contractEvents: EventData[] = []
|
||||
if (blockNumber > snapshotBlockNumber) {
|
||||
contractEvents = await contract.getPastEvents('RequiredSignaturesChanged', {
|
||||
contractEvents = await getPastEventsWithFallback(api, web3, contract, 'RequiredSignaturesChanged', {
|
||||
fromBlock: snapshotBlockNumber + 1,
|
||||
toBlock: blockNumber
|
||||
})
|
||||
@ -59,7 +76,13 @@ export const getRequiredSignatures = async (
|
||||
return parseInt(requiredSignatures)
|
||||
}
|
||||
|
||||
export const getValidatorList = async (contract: Contract, blockNumber: number, snapshotProvider: SnapshotProvider) => {
|
||||
export const getValidatorList = async (
|
||||
contract: Contract,
|
||||
blockNumber: number,
|
||||
snapshotProvider: SnapshotProvider,
|
||||
web3: Web3 | null = null,
|
||||
api: string = ''
|
||||
) => {
|
||||
const addedEventsFromSnapshot = snapshotProvider.validatorAddedEvents(blockNumber)
|
||||
const removedEventsFromSnapshot = snapshotProvider.validatorRemovedEvents(blockNumber)
|
||||
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
||||
@ -67,10 +90,10 @@ export const getValidatorList = async (contract: Contract, blockNumber: number,
|
||||
const fromBlock = snapshotBlockNumber > blockNumber ? snapshotBlockNumber + 1 : blockNumber
|
||||
const [currentList, added, removed] = await Promise.all([
|
||||
contract.methods.validatorList().call(),
|
||||
contract.getPastEvents('ValidatorAdded', {
|
||||
getPastEventsWithFallback(api, web3, contract, 'ValidatorAdded', {
|
||||
fromBlock
|
||||
}),
|
||||
contract.getPastEvents('ValidatorRemoved', {
|
||||
getPastEventsWithFallback(api, web3, contract, 'ValidatorRemoved', {
|
||||
fromBlock
|
||||
})
|
||||
])
|
||||
|
@ -7,6 +7,9 @@ import {
|
||||
MAX_TX_SEARCH_BLOCK_RANGE,
|
||||
SUBMIT_SIGNATURE_HASH
|
||||
} from '../config/constants'
|
||||
import { AbiItem } from 'web3-utils'
|
||||
import Web3 from 'web3'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
|
||||
export interface APITransaction {
|
||||
timeStamp: string
|
||||
@ -47,10 +50,15 @@ export interface GetTransactionParams extends GetPendingTransactionParams {
|
||||
}
|
||||
|
||||
export const fetchAccountTransactions = async ({ account, startBlock, endBlock, api }: AccountTransactionsParams) => {
|
||||
const params = `module=account&action=txlist&address=${account}&filterby=from&startblock=${startBlock}&endblock=${endBlock}`
|
||||
const url = api.includes('blockscout') ? `${api}?${params}` : `${api}&${params}`
|
||||
const url = new URL(api)
|
||||
url.searchParams.append('module', 'account')
|
||||
url.searchParams.append('action', 'txlist')
|
||||
url.searchParams.append('address', account)
|
||||
url.searchParams.append('filterby', 'from')
|
||||
url.searchParams.append('startblock', startBlock.toString())
|
||||
url.searchParams.append('endblock', endBlock.toString())
|
||||
|
||||
const result = await fetch(url).then(res => res.json())
|
||||
const result = await fetch(url.toString()).then(res => res.json())
|
||||
|
||||
if (result.message === 'No transactions found') {
|
||||
return []
|
||||
@ -66,10 +74,13 @@ export const fetchPendingTransactions = async ({
|
||||
if (!api.includes('blockscout')) {
|
||||
return []
|
||||
}
|
||||
const url = `${api}?module=account&action=pendingtxlist&address=${account}`
|
||||
const url = new URL(api)
|
||||
url.searchParams.append('module', 'account')
|
||||
url.searchParams.append('action', 'pendingtxlist')
|
||||
url.searchParams.append('address', account)
|
||||
|
||||
try {
|
||||
const result = await fetch(url).then(res => res.json())
|
||||
const result = await fetch(url.toString()).then(res => res.json())
|
||||
if (result.status === '0') {
|
||||
return []
|
||||
}
|
||||
@ -85,9 +96,13 @@ export const getClosestBlockByTimestamp = async (api: string, timestamp: number)
|
||||
throw new Error('Blockscout does not support getblocknobytime')
|
||||
}
|
||||
|
||||
const url = `${api}&module=block&action=getblocknobytime×tamp=${timestamp}&closest=before`
|
||||
const url = new URL(api)
|
||||
url.searchParams.append('module', 'block')
|
||||
url.searchParams.append('action', 'getblocknobytime')
|
||||
url.searchParams.append('timestamp', timestamp.toString())
|
||||
url.searchParams.append('closest', 'before')
|
||||
|
||||
const blockNumber = await fetch(url).then(res => res.json())
|
||||
const blockNumber = await fetch(url.toString()).then(res => res.json())
|
||||
|
||||
return parseInt(blockNumber.result)
|
||||
}
|
||||
@ -144,6 +159,41 @@ export const getAccountTransactions = async ({
|
||||
return transactionsCache[key].transactions
|
||||
}
|
||||
|
||||
export const getLogs = async (
|
||||
api: string,
|
||||
web3: Web3,
|
||||
contract: Contract,
|
||||
event: string,
|
||||
options: { fromBlock: number; toBlock: number | 'latest'; topics: (string | null)[] }
|
||||
) => {
|
||||
const abi = contract.options.jsonInterface.find((abi: AbiItem) => abi.type === 'event' && abi.name === event)!
|
||||
|
||||
const url = new URL(api)
|
||||
url.searchParams.append('module', 'logs')
|
||||
url.searchParams.append('action', 'getLogs')
|
||||
url.searchParams.append('address', contract.options.address)
|
||||
url.searchParams.append('fromBlock', options.fromBlock.toString())
|
||||
url.searchParams.append('toBlock', options.toBlock.toString() || 'latest')
|
||||
|
||||
const topics = [web3.eth.abi.encodeEventSignature(abi), ...(options.topics || [])]
|
||||
for (let i = 0; i < topics.length; i++) {
|
||||
if (topics[i] !== null) {
|
||||
url.searchParams.append(`topic${i}`, topics[i] as string)
|
||||
for (let j = 0; j < i; j++) {
|
||||
url.searchParams.append(`topic${j}_${i}_opr`, 'and')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const logs = await fetch(url.toString()).then(res => res.json())
|
||||
|
||||
return logs.result.map((log: any) => ({
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: parseInt(log.blockNumber.slice(2), 16),
|
||||
returnValues: web3.eth.abi.decodeLog(abi.inputs!, log.data, log.topics.slice(1))
|
||||
}))
|
||||
}
|
||||
|
||||
const filterReceiver = (to: string) => (tx: APITransaction) => tx.to.toLowerCase() === to.toLowerCase()
|
||||
|
||||
export const getFailedTransactions = async (
|
||||
|
@ -1,16 +1,45 @@
|
||||
import { Contract, EventData } from 'web3-eth-contract'
|
||||
import Web3 from 'web3'
|
||||
import { CACHE_KEY_EXECUTION_FAILED, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import {
|
||||
CACHE_KEY_EXECUTION_FAILED,
|
||||
FOREIGN_EXPLORER_API,
|
||||
HOME_EXPLORER_API,
|
||||
VALIDATOR_CONFIRMATION_STATUS
|
||||
} from '../config/constants'
|
||||
import { ExecutionData } from '../hooks/useMessageConfirmations'
|
||||
import { APIPendingTransaction, APITransaction, GetTransactionParams, GetPendingTransactionParams } from './explorer'
|
||||
import {
|
||||
APIPendingTransaction,
|
||||
APITransaction,
|
||||
GetTransactionParams,
|
||||
GetPendingTransactionParams,
|
||||
getLogs
|
||||
} from './explorer'
|
||||
import { getBlock, MessageObject } from './web3'
|
||||
import validatorsCache from '../services/ValidatorsCache'
|
||||
import { foreignBlockNumberProvider, homeBlockNumberProvider } from '../services/BlockNumberProvider'
|
||||
|
||||
export const getSuccessExecutionData = async (contract: Contract, eventName: string, web3: Web3, messageId: string) => {
|
||||
const getPastEventsWithFallback = (api: string, web3: Web3, contract: Contract, eventName: string, options: any) =>
|
||||
contract.getPastEvents(eventName, options).catch(
|
||||
() =>
|
||||
api
|
||||
? getLogs(api, web3, contract, eventName, {
|
||||
fromBlock: options.fromBlock,
|
||||
toBlock: options.toBlock,
|
||||
topics: [null, null, options.filter.messageId]
|
||||
})
|
||||
: []
|
||||
)
|
||||
|
||||
export const getSuccessExecutionData = async (
|
||||
contract: Contract,
|
||||
eventName: string,
|
||||
web3: Web3,
|
||||
messageId: string,
|
||||
api: string = ''
|
||||
) => {
|
||||
// Since it filters by the message id, only one event will be fetched
|
||||
// so there is no need to limit the range of the block to reduce the network traffic
|
||||
const events: EventData[] = await contract.getPastEvents(eventName, {
|
||||
const events: EventData[] = await getPastEventsWithFallback(api, web3, contract, eventName, {
|
||||
fromBlock: 0,
|
||||
toBlock: 'latest',
|
||||
filter: {
|
||||
@ -57,7 +86,8 @@ export const getFinalizationEvent = async (
|
||||
setExecutionEventsFetched: Function
|
||||
) => {
|
||||
if (!contract || !web3 || !waitingBlocksResolved) return
|
||||
const successExecutionData = await getSuccessExecutionData(contract, eventName, web3, message.id)
|
||||
const api = fromHome ? FOREIGN_EXPLORER_API : HOME_EXPLORER_API
|
||||
const successExecutionData = await getSuccessExecutionData(contract, eventName, web3, message.id, api)
|
||||
if (successExecutionData) {
|
||||
setResult(successExecutionData)
|
||||
} else {
|
||||
|
@ -13953,6 +13953,11 @@ node-fetch@^2.1.2, node-fetch@^2.3.0, node-fetch@^2.5.0:
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
|
||||
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-forge@0.7.5:
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
|
||||
|
Loading…
Reference in New Issue
Block a user