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 commons/package.json ./commons/
|
||||||
COPY alm/package.json ./alm/
|
COPY alm/package.json ./alm/
|
||||||
COPY yarn.lock .
|
COPY yarn.lock .
|
||||||
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production
|
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile
|
||||||
|
|
||||||
COPY ./commons ./commons
|
COPY ./commons ./commons
|
||||||
COPY ./alm ./alm
|
COPY ./alm ./alm
|
||||||
|
@ -58,6 +58,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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')
|
const path = require('path')
|
||||||
require('dotenv').config()
|
require('dotenv').config()
|
||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
|
const fetch = require('node-fetch')
|
||||||
|
const { URL } = require('url')
|
||||||
|
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
|
||||||
@ -10,7 +12,9 @@ const {
|
|||||||
COMMON_HOME_RPC_URL,
|
COMMON_HOME_RPC_URL,
|
||||||
COMMON_HOME_BRIDGE_ADDRESS,
|
COMMON_HOME_BRIDGE_ADDRESS,
|
||||||
COMMON_FOREIGN_RPC_URL,
|
COMMON_FOREIGN_RPC_URL,
|
||||||
COMMON_FOREIGN_BRIDGE_ADDRESS
|
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||||
|
ALM_FOREIGN_EXPLORER_API,
|
||||||
|
ALM_HOME_EXPLORER_API
|
||||||
} = process.env
|
} = process.env
|
||||||
|
|
||||||
const generateSnapshot = async (side, url, bridgeAddress) => {
|
const generateSnapshot = async (side, url, bridgeAddress) => {
|
||||||
@ -19,6 +23,31 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
|||||||
const snapshot = {}
|
const snapshot = {}
|
||||||
|
|
||||||
const web3 = new Web3(new Web3.providers.HttpProvider(url))
|
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()
|
const currentBlockNumber = await web3.eth.getBlockNumber()
|
||||||
snapshot.snapshotBlockNumber = currentBlockNumber
|
snapshot.snapshotBlockNumber = currentBlockNumber
|
||||||
@ -29,10 +58,14 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
|||||||
const bridgeContract = new web3.eth.Contract(HOME_AMB_ABI, bridgeAddress)
|
const bridgeContract = new web3.eth.Contract(HOME_AMB_ABI, bridgeAddress)
|
||||||
|
|
||||||
// Save RequiredBlockConfirmationChanged events
|
// Save RequiredBlockConfirmationChanged events
|
||||||
let requiredBlockConfirmationChangedEvents = await bridgeContract.getPastEvents('RequiredBlockConfirmationChanged', {
|
let requiredBlockConfirmationChangedEvents = await getPastEventsWithFallback(
|
||||||
fromBlock: 0,
|
bridgeContract,
|
||||||
toBlock: currentBlockNumber
|
'RequiredBlockConfirmationChanged',
|
||||||
})
|
{
|
||||||
|
fromBlock: 0,
|
||||||
|
toBlock: currentBlockNumber
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// In case RequiredBlockConfirmationChanged was not emitted during initialization in early versions of AMB
|
// In case RequiredBlockConfirmationChanged was not emitted during initialization in early versions of AMB
|
||||||
// manually generate an event for this. Example Sokol - Kovan bridge
|
// 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)
|
const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorAddress)
|
||||||
|
|
||||||
// Save RequiredSignaturesChanged events
|
// Save RequiredSignaturesChanged events
|
||||||
const RequiredSignaturesChangedEvents = await validatorContract.getPastEvents('RequiredSignaturesChanged', {
|
const RequiredSignaturesChangedEvents = await getPastEventsWithFallback(
|
||||||
fromBlock: 0,
|
validatorContract,
|
||||||
toBlock: currentBlockNumber
|
'RequiredSignaturesChanged',
|
||||||
})
|
{
|
||||||
|
fromBlock: 0,
|
||||||
|
toBlock: currentBlockNumber
|
||||||
|
}
|
||||||
|
)
|
||||||
snapshot.RequiredSignaturesChanged = RequiredSignaturesChangedEvents.map(e => ({
|
snapshot.RequiredSignaturesChanged = RequiredSignaturesChangedEvents.map(e => ({
|
||||||
blockNumber: e.blockNumber,
|
blockNumber: e.blockNumber,
|
||||||
returnValues: {
|
returnValues: {
|
||||||
@ -71,7 +108,7 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
// Save ValidatorAdded events
|
// Save ValidatorAdded events
|
||||||
const validatorAddedEvents = await validatorContract.getPastEvents('ValidatorAdded', {
|
const validatorAddedEvents = await getPastEventsWithFallback(validatorContract, 'ValidatorAdded', {
|
||||||
fromBlock: 0,
|
fromBlock: 0,
|
||||||
toBlock: currentBlockNumber
|
toBlock: currentBlockNumber
|
||||||
})
|
})
|
||||||
@ -85,7 +122,7 @@ const generateSnapshot = async (side, url, bridgeAddress) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
// Save ValidatorRemoved events
|
// Save ValidatorRemoved events
|
||||||
const validatorRemovedEvents = await validatorContract.getPastEvents('ValidatorRemoved', {
|
const validatorRemovedEvents = await getPastEventsWithFallback(validatorContract, 'ValidatorRemoved', {
|
||||||
fromBlock: 0,
|
fromBlock: 0,
|
||||||
toBlock: currentBlockNumber
|
toBlock: currentBlockNumber
|
||||||
})
|
})
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
DOUBLE_EXECUTION_ATTEMPT_ERROR,
|
DOUBLE_EXECUTION_ATTEMPT_ERROR,
|
||||||
EXECUTION_FAILED_ERROR,
|
EXECUTION_FAILED_ERROR,
|
||||||
EXECUTION_OUT_OF_GAS_ERROR,
|
EXECUTION_OUT_OF_GAS_ERROR,
|
||||||
|
FOREIGN_EXPLORER_API,
|
||||||
INCORRECT_CHAIN_ERROR,
|
INCORRECT_CHAIN_ERROR,
|
||||||
VALIDATOR_CONFIRMATION_STATUS
|
VALIDATOR_CONFIRMATION_STATUS
|
||||||
} from '../config/constants'
|
} from '../config/constants'
|
||||||
@ -92,7 +93,13 @@ export const ManualExecutionButton = ({
|
|||||||
})
|
})
|
||||||
.on('error', async (e: Error, receipt: TransactionReceipt) => {
|
.on('error', async (e: Error, receipt: TransactionReceipt) => {
|
||||||
if (e.message.includes('Transaction has been reverted by the EVM')) {
|
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) {
|
if (successExecutionData) {
|
||||||
setExecutionData(successExecutionData)
|
setExecutionData(successExecutionData)
|
||||||
setError(DOUBLE_EXECUTION_ATTEMPT_ERROR)
|
setError(DOUBLE_EXECUTION_ATTEMPT_ERROR)
|
||||||
|
@ -4,6 +4,8 @@ import { useStateProvider } from '../state/StateProvider'
|
|||||||
import { Contract } from 'web3-eth-contract'
|
import { Contract } from 'web3-eth-contract'
|
||||||
import { getRequiredBlockConfirmations } from '../utils/contract'
|
import { getRequiredBlockConfirmations } from '../utils/contract'
|
||||||
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
|
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
|
||||||
|
import Web3 from 'web3'
|
||||||
|
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
|
||||||
|
|
||||||
export interface UseBlockConfirmationsParams {
|
export interface UseBlockConfirmationsParams {
|
||||||
fromHome: boolean
|
fromHome: boolean
|
||||||
@ -19,9 +21,11 @@ export const useBlockConfirmations = ({ receipt, fromHome }: UseBlockConfirmatio
|
|||||||
contract: Contract,
|
contract: Contract,
|
||||||
receipt: TransactionReceipt,
|
receipt: TransactionReceipt,
|
||||||
setResult: Function,
|
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)
|
setResult(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,10 +33,12 @@ export const useBlockConfirmations = ({ receipt, fromHome }: UseBlockConfirmatio
|
|||||||
() => {
|
() => {
|
||||||
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
||||||
const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider
|
const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider
|
||||||
if (!bridgeContract || !receipt) return
|
const web3 = fromHome ? home.web3 : foreign.web3
|
||||||
callRequireBlockConfirmations(bridgeContract, receipt, setBlockConfirmations, snapshotProvider)
|
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 {
|
return {
|
||||||
|
@ -6,6 +6,7 @@ import { BRIDGE_VALIDATORS_ABI } from '../abis'
|
|||||||
import { useStateProvider } from '../state/StateProvider'
|
import { useStateProvider } from '../state/StateProvider'
|
||||||
import { TransactionReceipt } from 'web3-eth'
|
import { TransactionReceipt } from 'web3-eth'
|
||||||
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
|
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
|
||||||
|
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
|
||||||
|
|
||||||
export interface useValidatorContractParams {
|
export interface useValidatorContractParams {
|
||||||
fromHome: boolean
|
fromHome: boolean
|
||||||
@ -30,10 +31,12 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
|
|||||||
contract: Maybe<Contract>,
|
contract: Maybe<Contract>,
|
||||||
receipt: TransactionReceipt,
|
receipt: TransactionReceipt,
|
||||||
setResult: Function,
|
setResult: Function,
|
||||||
snapshotProvider: SnapshotProvider
|
snapshotProvider: SnapshotProvider,
|
||||||
|
web3: Web3,
|
||||||
|
api: string
|
||||||
) => {
|
) => {
|
||||||
if (!contract) return
|
if (!contract) return
|
||||||
const result = await getRequiredSignatures(contract, receipt.blockNumber, snapshotProvider)
|
const result = await getRequiredSignatures(contract, receipt.blockNumber, snapshotProvider, web3, api)
|
||||||
setResult(result)
|
setResult(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,32 +44,35 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
|
|||||||
contract: Maybe<Contract>,
|
contract: Maybe<Contract>,
|
||||||
receipt: TransactionReceipt,
|
receipt: TransactionReceipt,
|
||||||
setResult: Function,
|
setResult: Function,
|
||||||
snapshotProvider: SnapshotProvider
|
snapshotProvider: SnapshotProvider,
|
||||||
|
web3: Web3,
|
||||||
|
api: string
|
||||||
) => {
|
) => {
|
||||||
if (!contract) return
|
if (!contract) return
|
||||||
const result = await getValidatorList(contract, receipt.blockNumber, snapshotProvider)
|
const result = await getValidatorList(contract, receipt.blockNumber, snapshotProvider, web3, api)
|
||||||
setResult(result)
|
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(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
const web3 = fromHome ? home.web3 : foreign.web3
|
|
||||||
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
|
||||||
|
|
||||||
if (!web3 || !bridgeContract) return
|
if (!web3 || !bridgeContract) return
|
||||||
callValidatorContract(bridgeContract, web3, setValidatorContract)
|
callValidatorContract(bridgeContract, web3, setValidatorContract)
|
||||||
},
|
},
|
||||||
[home.web3, foreign.web3, home.bridgeContract, foreign.bridgeContract, fromHome]
|
[web3, bridgeContract]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
if (!receipt) return
|
if (!web3 || !receipt) return
|
||||||
const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider
|
callRequiredSignatures(validatorContract, receipt, setRequiredSignatures, snapshotProvider, web3, api)
|
||||||
callRequiredSignatures(validatorContract, receipt, setRequiredSignatures, snapshotProvider)
|
callValidatorList(validatorContract, receipt, setValidatorList, snapshotProvider, web3, api)
|
||||||
callValidatorList(validatorContract, receipt, setValidatorList, snapshotProvider)
|
|
||||||
},
|
},
|
||||||
[validatorContract, receipt, fromHome]
|
[validatorContract, receipt, web3, snapshotProvider, api]
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -16,7 +16,7 @@ describe('getRequiredBlockConfirmations', () => {
|
|||||||
|
|
||||||
test('Should call requiredBlockConfirmations method if no events present', async () => {
|
test('Should call requiredBlockConfirmations method if no events present', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: () => {
|
getPastEvents: async () => {
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
methods: methodsBuilder('1')
|
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 () => {
|
test('Should not call to get events if block number was included in the snapshot', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => []),
|
getPastEvents: jest.fn().mockImplementation(async () => []),
|
||||||
methods: methodsBuilder('3')
|
methods: methodsBuilder('3')
|
||||||
} as unknown) as Contract
|
} 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 () => {
|
test('Should call to get events if block number was not included in the snapshot', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => [
|
getPastEvents: jest.fn().mockImplementation(async () => [
|
||||||
{
|
{
|
||||||
blockNumber: 9,
|
blockNumber: 9,
|
||||||
returnValues: {
|
returnValues: {
|
||||||
@ -102,7 +102,7 @@ describe('getRequiredBlockConfirmations', () => {
|
|||||||
})
|
})
|
||||||
test('Should use the most updated event', async () => {
|
test('Should use the most updated event', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => [
|
getPastEvents: jest.fn().mockImplementation(async () => [
|
||||||
{
|
{
|
||||||
blockNumber: 9,
|
blockNumber: 9,
|
||||||
returnValues: {
|
returnValues: {
|
||||||
@ -141,7 +141,7 @@ describe('getRequiredBlockConfirmations', () => {
|
|||||||
describe('getRequiredSignatures', () => {
|
describe('getRequiredSignatures', () => {
|
||||||
test('Should not call to get events if block number was included in the snapshot', async () => {
|
test('Should not call to get events if block number was included in the snapshot', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => [])
|
getPastEvents: jest.fn().mockImplementation(async () => [])
|
||||||
} as unknown) as Contract
|
} as unknown) as Contract
|
||||||
|
|
||||||
const snapshotProvider = ({
|
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 () => {
|
test('Should call to get events if block number is higher than the snapshot block number', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => [
|
getPastEvents: jest.fn().mockImplementation(async () => [
|
||||||
{
|
{
|
||||||
blockNumber: 15,
|
blockNumber: 15,
|
||||||
returnValues: {
|
returnValues: {
|
||||||
@ -216,7 +216,7 @@ describe('getRequiredSignatures', () => {
|
|||||||
})
|
})
|
||||||
test('Should use the most updated event before the block number', async () => {
|
test('Should use the most updated event before the block number', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => [
|
getPastEvents: jest.fn().mockImplementation(async () => [
|
||||||
{
|
{
|
||||||
blockNumber: 15,
|
blockNumber: 15,
|
||||||
returnValues: {
|
returnValues: {
|
||||||
@ -270,7 +270,7 @@ describe('getValidatorList', () => {
|
|||||||
test('Should return the current validator list if no events found', async () => {
|
test('Should return the current validator list if no events found', async () => {
|
||||||
const currentValidators = [validator1, validator2, validator3]
|
const currentValidators = [validator1, validator2, validator3]
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => []),
|
getPastEvents: jest.fn().mockImplementation(async () => []),
|
||||||
methods: methodsBuilder(currentValidators)
|
methods: methodsBuilder(currentValidators)
|
||||||
} as unknown) as Contract
|
} as unknown) as Contract
|
||||||
|
|
||||||
@ -301,7 +301,7 @@ describe('getValidatorList', () => {
|
|||||||
test('If validator was added later from snapshot it should not include it', async () => {
|
test('If validator was added later from snapshot it should not include it', async () => {
|
||||||
const currentValidators = [validator1, validator2, validator3]
|
const currentValidators = [validator1, validator2, validator3]
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => []),
|
getPastEvents: jest.fn().mockImplementation(async () => []),
|
||||||
methods: methodsBuilder(currentValidators)
|
methods: methodsBuilder(currentValidators)
|
||||||
} as unknown) as Contract
|
} as unknown) as Contract
|
||||||
|
|
||||||
@ -340,7 +340,7 @@ describe('getValidatorList', () => {
|
|||||||
test('If validator was added later from chain it should not include it', async () => {
|
test('If validator was added later from chain it should not include it', async () => {
|
||||||
const currentValidators = [validator1, validator2, validator3]
|
const currentValidators = [validator1, validator2, validator3]
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(event => {
|
getPastEvents: jest.fn().mockImplementation(async event => {
|
||||||
if (event === 'ValidatorAdded') {
|
if (event === 'ValidatorAdded') {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -385,7 +385,7 @@ describe('getValidatorList', () => {
|
|||||||
test('If validator was removed later from snapshot it should include it', async () => {
|
test('If validator was removed later from snapshot it should include it', async () => {
|
||||||
const currentValidators = [validator1, validator2]
|
const currentValidators = [validator1, validator2]
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(() => []),
|
getPastEvents: jest.fn().mockImplementation(async () => []),
|
||||||
methods: methodsBuilder(currentValidators)
|
methods: methodsBuilder(currentValidators)
|
||||||
} as unknown) as Contract
|
} as unknown) as Contract
|
||||||
|
|
||||||
@ -424,7 +424,7 @@ describe('getValidatorList', () => {
|
|||||||
test('If validator was removed later from chain it should include it', async () => {
|
test('If validator was removed later from chain it should include it', async () => {
|
||||||
const currentValidators = [validator1, validator2]
|
const currentValidators = [validator1, validator2]
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: jest.fn().mockImplementation(event => {
|
getPastEvents: jest.fn().mockImplementation(async event => {
|
||||||
if (event === 'ValidatorRemoved') {
|
if (event === 'ValidatorRemoved') {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -50,7 +50,7 @@ beforeEach(() => {
|
|||||||
describe('getFinalizationEvent', () => {
|
describe('getFinalizationEvent', () => {
|
||||||
test('should get finalization event and not try to get failed or pending transactions', async () => {
|
test('should get finalization event and not try to get failed or pending transactions', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: () => {
|
getPastEvents: async () => {
|
||||||
return [event]
|
return [event]
|
||||||
}
|
}
|
||||||
} as unknown) as Contract
|
} 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 () => {
|
test('should retry to get finalization event and not try to get failed or pending transactions if foreign to home transaction', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: () => {
|
getPastEvents: async () => {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
} as unknown) as Contract
|
} 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 () => {
|
test('should retry to get finalization event and try to get failed and pending transactions if home to foreign transaction', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: () => {
|
getPastEvents: async () => {
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
options: {
|
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 () => {
|
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 = ({
|
const contract = ({
|
||||||
getPastEvents: () => {
|
getPastEvents: async () => {
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
options: {
|
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 () => {
|
test('should retry to get finalization event even if failed transaction found if home to foreign transaction', async () => {
|
||||||
const contract = ({
|
const contract = ({
|
||||||
getPastEvents: () => {
|
getPastEvents: async () => {
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
|
@ -1,18 +1,33 @@
|
|||||||
import { Contract } from 'web3-eth-contract'
|
import { Contract } from 'web3-eth-contract'
|
||||||
import { EventData } from 'web3-eth-contract'
|
import { EventData } from 'web3-eth-contract'
|
||||||
import { SnapshotProvider } from '../services/SnapshotProvider'
|
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 (
|
export const getRequiredBlockConfirmations = async (
|
||||||
contract: Contract,
|
contract: Contract,
|
||||||
blockNumber: number,
|
blockNumber: number,
|
||||||
snapshotProvider: SnapshotProvider
|
snapshotProvider: SnapshotProvider,
|
||||||
|
web3: Web3 | null = null,
|
||||||
|
api: string = ''
|
||||||
) => {
|
) => {
|
||||||
const eventsFromSnapshot = snapshotProvider.requiredBlockConfirmationEvents(blockNumber)
|
const eventsFromSnapshot = snapshotProvider.requiredBlockConfirmationEvents(blockNumber)
|
||||||
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
||||||
|
|
||||||
let contractEvents: EventData[] = []
|
let contractEvents: EventData[] = []
|
||||||
if (blockNumber > snapshotBlockNumber) {
|
if (blockNumber > snapshotBlockNumber) {
|
||||||
contractEvents = await contract.getPastEvents('RequiredBlockConfirmationChanged', {
|
contractEvents = await getPastEventsWithFallback(api, web3, contract, 'RequiredBlockConfirmationChanged', {
|
||||||
fromBlock: snapshotBlockNumber + 1,
|
fromBlock: snapshotBlockNumber + 1,
|
||||||
toBlock: blockNumber
|
toBlock: blockNumber
|
||||||
})
|
})
|
||||||
@ -38,14 +53,16 @@ export const getValidatorAddress = (contract: Contract) => contract.methods.vali
|
|||||||
export const getRequiredSignatures = async (
|
export const getRequiredSignatures = async (
|
||||||
contract: Contract,
|
contract: Contract,
|
||||||
blockNumber: number,
|
blockNumber: number,
|
||||||
snapshotProvider: SnapshotProvider
|
snapshotProvider: SnapshotProvider,
|
||||||
|
web3: Web3 | null = null,
|
||||||
|
api: string = ''
|
||||||
) => {
|
) => {
|
||||||
const eventsFromSnapshot = snapshotProvider.requiredSignaturesEvents(blockNumber)
|
const eventsFromSnapshot = snapshotProvider.requiredSignaturesEvents(blockNumber)
|
||||||
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
||||||
|
|
||||||
let contractEvents: EventData[] = []
|
let contractEvents: EventData[] = []
|
||||||
if (blockNumber > snapshotBlockNumber) {
|
if (blockNumber > snapshotBlockNumber) {
|
||||||
contractEvents = await contract.getPastEvents('RequiredSignaturesChanged', {
|
contractEvents = await getPastEventsWithFallback(api, web3, contract, 'RequiredSignaturesChanged', {
|
||||||
fromBlock: snapshotBlockNumber + 1,
|
fromBlock: snapshotBlockNumber + 1,
|
||||||
toBlock: blockNumber
|
toBlock: blockNumber
|
||||||
})
|
})
|
||||||
@ -59,7 +76,13 @@ export const getRequiredSignatures = async (
|
|||||||
return parseInt(requiredSignatures)
|
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 addedEventsFromSnapshot = snapshotProvider.validatorAddedEvents(blockNumber)
|
||||||
const removedEventsFromSnapshot = snapshotProvider.validatorRemovedEvents(blockNumber)
|
const removedEventsFromSnapshot = snapshotProvider.validatorRemovedEvents(blockNumber)
|
||||||
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
||||||
@ -67,10 +90,10 @@ export const getValidatorList = async (contract: Contract, blockNumber: number,
|
|||||||
const fromBlock = snapshotBlockNumber > blockNumber ? snapshotBlockNumber + 1 : blockNumber
|
const fromBlock = snapshotBlockNumber > blockNumber ? snapshotBlockNumber + 1 : blockNumber
|
||||||
const [currentList, added, removed] = await Promise.all([
|
const [currentList, added, removed] = await Promise.all([
|
||||||
contract.methods.validatorList().call(),
|
contract.methods.validatorList().call(),
|
||||||
contract.getPastEvents('ValidatorAdded', {
|
getPastEventsWithFallback(api, web3, contract, 'ValidatorAdded', {
|
||||||
fromBlock
|
fromBlock
|
||||||
}),
|
}),
|
||||||
contract.getPastEvents('ValidatorRemoved', {
|
getPastEventsWithFallback(api, web3, contract, 'ValidatorRemoved', {
|
||||||
fromBlock
|
fromBlock
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
|
@ -7,6 +7,9 @@ import {
|
|||||||
MAX_TX_SEARCH_BLOCK_RANGE,
|
MAX_TX_SEARCH_BLOCK_RANGE,
|
||||||
SUBMIT_SIGNATURE_HASH
|
SUBMIT_SIGNATURE_HASH
|
||||||
} from '../config/constants'
|
} from '../config/constants'
|
||||||
|
import { AbiItem } from 'web3-utils'
|
||||||
|
import Web3 from 'web3'
|
||||||
|
import { Contract } from 'web3-eth-contract'
|
||||||
|
|
||||||
export interface APITransaction {
|
export interface APITransaction {
|
||||||
timeStamp: string
|
timeStamp: string
|
||||||
@ -47,10 +50,15 @@ export interface GetTransactionParams extends GetPendingTransactionParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const fetchAccountTransactions = async ({ account, startBlock, endBlock, api }: AccountTransactionsParams) => {
|
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 = new URL(api)
|
||||||
const url = api.includes('blockscout') ? `${api}?${params}` : `${api}&${params}`
|
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') {
|
if (result.message === 'No transactions found') {
|
||||||
return []
|
return []
|
||||||
@ -66,10 +74,13 @@ export const fetchPendingTransactions = async ({
|
|||||||
if (!api.includes('blockscout')) {
|
if (!api.includes('blockscout')) {
|
||||||
return []
|
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 {
|
try {
|
||||||
const result = await fetch(url).then(res => res.json())
|
const result = await fetch(url.toString()).then(res => res.json())
|
||||||
if (result.status === '0') {
|
if (result.status === '0') {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -85,9 +96,13 @@ export const getClosestBlockByTimestamp = async (api: string, timestamp: number)
|
|||||||
throw new Error('Blockscout does not support getblocknobytime')
|
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)
|
return parseInt(blockNumber.result)
|
||||||
}
|
}
|
||||||
@ -144,6 +159,41 @@ export const getAccountTransactions = async ({
|
|||||||
return transactionsCache[key].transactions
|
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()
|
const filterReceiver = (to: string) => (tx: APITransaction) => tx.to.toLowerCase() === to.toLowerCase()
|
||||||
|
|
||||||
export const getFailedTransactions = async (
|
export const getFailedTransactions = async (
|
||||||
|
@ -1,16 +1,45 @@
|
|||||||
import { Contract, EventData } from 'web3-eth-contract'
|
import { Contract, EventData } from 'web3-eth-contract'
|
||||||
import Web3 from 'web3'
|
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 { 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 { getBlock, MessageObject } from './web3'
|
||||||
import validatorsCache from '../services/ValidatorsCache'
|
import validatorsCache from '../services/ValidatorsCache'
|
||||||
import { foreignBlockNumberProvider, homeBlockNumberProvider } from '../services/BlockNumberProvider'
|
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
|
// 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
|
// 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,
|
fromBlock: 0,
|
||||||
toBlock: 'latest',
|
toBlock: 'latest',
|
||||||
filter: {
|
filter: {
|
||||||
@ -57,7 +86,8 @@ export const getFinalizationEvent = async (
|
|||||||
setExecutionEventsFetched: Function
|
setExecutionEventsFetched: Function
|
||||||
) => {
|
) => {
|
||||||
if (!contract || !web3 || !waitingBlocksResolved) return
|
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) {
|
if (successExecutionData) {
|
||||||
setResult(successExecutionData)
|
setResult(successExecutionData)
|
||||||
} else {
|
} 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"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
|
||||||
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
|
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:
|
node-forge@0.7.5:
|
||||||
version "0.7.5"
|
version "0.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
|
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
|
||||||
|
Loading…
Reference in New Issue
Block a user