diff --git a/.env.jungle b/.env.jungle index fa2f4b9e1..0249458bc 100644 --- a/.env.jungle +++ b/.env.jungle @@ -1,6 +1,6 @@ # global STAGE=dev -APP_NAME=eosio-dashboard +APP_NAME=antelope-tools # wallet WALLET_DATA=./wallet_data @@ -56,7 +56,27 @@ HAPI_RE_CAPTCHA_PROJECT_ID= HAPI_PUBLIC_RE_CAPTCHA_KEY= HAPI_CREATE_ACCOUNT_ACTION_NAME= -#webapp +# hapi-evm +HAPI_EVM_SERVER_PORT=9090 +HAPI_EVM_SERVER_ADDRESS=hapi-evm +HAPI_EVM_HASURA_URL=http://hasura:8080/v1/graphql +HAPI_EVM_HASURA_ADMIN_SECRET=myadminsecretkey +HAPI_EVM_DATABASE_URL=postgres://eoscr:password@postgres:5432/localdb +HAPI_EVM_ENDPOINT=https://api.testnet.evm.eosnetwork.com +HAPI_EVM_API_ENDPOINTS=["https://jungle.edenia.cloud","https://jungle4.eosphere.io","https://jungle4.api.eosnation.io","https://jungle4.eossweden.org"] +HAPI_EVM_NETWORK=EOSIO# EOSIO, TELOS +HAPI_EVM_NETWORK_CHAIN_ID=73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d +HAPI_EVM_EOS_EVM_ACCOUNT=eosio.evm +HAPI_EVM_BLOCK_INTERVAL_SEC=1 +HAPI_EVM_OLD_BLOCK_INTERVAL_SEC=0.1 +HAPI_EVM_ATH_INTERVAL_SEC=60 +HAPI_EVM_CLEAN_OLD_BLOCK_INTERVAL_SEC=86400 +HAPI_EVM_CLEAN_OLD_TRANSFER_INTERVAL_SEC=86400 +HAPI_EVM_KEEP_HISTORY_FOR_YEARS=1 +HAPI_EVM_HYPERION_API=https://jungle.eosusa.io +HAPI_EVM_HYPERION_START_AT=2023-10-25T00:00:00.000+00:00 + +# webapp PORT=3000 REACT_APP_VERSION=dev REACT_APP_NAME=$npm_package_name diff --git a/.env.local b/.env.local index e76763335..55b1d3e0b 100644 --- a/.env.local +++ b/.env.local @@ -1,6 +1,6 @@ # global STAGE=dev -APP_NAME=eosio-dashboard +APP_NAME=antelope-tools # wallet WALLET_DATA=./wallet_data diff --git a/.env.telostestnet b/.env.telostestnet index 99b01ce59..7e272d30d 100644 --- a/.env.telostestnet +++ b/.env.telostestnet @@ -1,6 +1,6 @@ # global STAGE=dev -APP_NAME=eosio-dashboard +APP_NAME=antelope-tools # wallet WALLET_DATA=./wallet_data @@ -76,7 +76,7 @@ HAPI_EVM_KEEP_HISTORY_FOR_YEARS=1 HAPI_EVM_HYPERION_API=https://test.telos.eosusa.io HAPI_EVM_HYPERION_START_AT=2021-06-02T00:00:00.000+00:00 -#webapp +# webapp PORT=3000 REACT_APP_VERSION=dev REACT_APP_NAME=$npm_package_name diff --git a/.env.ultratestnet b/.env.ultratestnet index 413339815..b4afef4c6 100644 --- a/.env.ultratestnet +++ b/.env.ultratestnet @@ -1,6 +1,6 @@ # global STAGE=dev -APP_NAME=eosio-dashboard +APP_NAME=antelope-tools # wallet WALLET_DATA=./wallet_data diff --git a/docker-compose.yaml b/docker-compose.yaml index 805e5ffdc..520cda6b9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -91,6 +91,7 @@ services: HAPI_EVM_DATABASE_URL: '${HAPI_EVM_DATABASE_URL}' HAPI_EVM_ENDPOINT: '${HAPI_EVM_ENDPOINT}' HAPI_EVM_API_ENDPOINTS: '${HAPI_EVM_API_ENDPOINTS}' + HAPI_EVM_NETWORK: '${HAPI_EVM_NETWORK}' HAPI_EVM_NETWORK_CHAIN_ID: '${HAPI_EVM_NETWORK_CHAIN_ID}' HAPI_EVM_EOS_EVM_ACCOUNT: '${HAPI_EVM_EOS_EVM_ACCOUNT}' HAPI_EVM_BLOCK_INTERVAL_SEC: '${HAPI_EVM_BLOCK_INTERVAL_SEC}' diff --git a/hapi-evm/src/config/network.config.ts b/hapi-evm/src/config/network.config.ts index e97f6b724..ba1e56a97 100644 --- a/hapi-evm/src/config/network.config.ts +++ b/hapi-evm/src/config/network.config.ts @@ -1,5 +1,9 @@ +import { AllowedNetworkType } from '../models/default.model' + export const evmEndpoint = process.env.HAPI_EVM_ENDPOINT || 'http://localhost/evm' +export const network: AllowedNetworkType = + (process.env.HAPI_EVM_NETWORK as AllowedNetworkType) || 'EOSIO' export const chainId = process.env.HAPI_EVM_NETWORK_CHAIN_ID || 'chainid1' export const evmAccount = process.env.HAPI_EVM_EOS_EVM_ACCOUNT || 'eosio.evm' export const eosEndpoints = diff --git a/hapi-evm/src/models/default.model.ts b/hapi-evm/src/models/default.model.ts index 4e2cf9279..6c4f377d7 100644 --- a/hapi-evm/src/models/default.model.ts +++ b/hapi-evm/src/models/default.model.ts @@ -18,3 +18,16 @@ export interface Worker { intervalSec?: number action: () => Promise } + +export const AllowedNetwork = { + EOSIO: 'EOSIO', + TELOS: 'TELOS' +} as const + +export type AllowedNetworkType = keyof typeof AllowedNetwork + +export interface BuilderListener { + type: string + notified_account: string + apply: (action: any) => Promise +} diff --git a/hapi-evm/src/services/block.service.ts b/hapi-evm/src/services/block.service.ts index e8b6fa812..22a6a65f0 100644 --- a/hapi-evm/src/services/block.service.ts +++ b/hapi-evm/src/services/block.service.ts @@ -15,20 +15,6 @@ import moment from 'moment' const httpProvider = new Web3.providers.HttpProvider(networkConfig.evmEndpoint) const web3 = new Web3(httpProvider) -// const test = async () => { -// const tempBlock: Block = await web3.eth.getBlock(0) -// console.log('🚀 ~ tempBlock:', tempBlock) - -// const trx: TransactionInfo = await web3.eth.getTransaction( -// '0x4b00d79018d46210b31829285541ae72653e03229a9cff67f362416e5a1c274c' -// ) -// console.log('🚀 ~ trx:', trx) -// console.log('🚀 ~ gas:', Number(trx.gas)) -// } - -// test() - -// TODO: syncronize passed blocks const syncFullBlock = async (blockNumber: number | bigint) => { const block: Block = await web3.eth.getBlock(blockNumber) @@ -80,8 +66,6 @@ const syncFullBlock = async (blockNumber: number | bigint) => { await incrementTotalTransactions(block.transactions?.length) - // TODO: review this logic - const transactionsPromises = [ cappedBlock.transactions.reduce( async ( @@ -143,27 +127,21 @@ const getBlock = async () => { const syncOldBlocks = async (): Promise => { const paramStats = await paramModel.queries.getState() - if (paramStats.isSynced) return - const nextBlock = paramStats.nextBlock const isUpToDate = await blockModel.queries.default.get({ number: { _eq: nextBlock } }) - if (!isUpToDate) { const nextBlockTo = await blockModel.queries.default.getNextBlock(nextBlock) const nextBlockToNumber = nextBlockTo[0]?.number || 0 - if (nextBlockToNumber > nextBlock) { console.log( `🚦 Syncing blocks behind, pending ${nextBlockToNumber - nextBlock} ` ) } - await syncFullBlock(nextBlock) } - await paramModel.queries.saveOrUpdate( nextBlock + 1 * Number(!isUpToDate), !!isUpToDate @@ -181,9 +159,7 @@ const cleanOldBlocks = async () => { const syncATH = async () => { const currentState = await historicalStatsModel.queries.getState() const partialATH = await StatsModel.queries.getPartialATH() - if (!partialATH) return - if ( currentState.tps_all_time_high.transactions_count || 0 < partialATH.ath_transactions_count diff --git a/hapi-evm/src/services/hyperion/index.ts b/hapi-evm/src/services/hyperion/index.ts index 59c180925..230233edd 100644 --- a/hapi-evm/src/services/hyperion/index.ts +++ b/hapi-evm/src/services/hyperion/index.ts @@ -2,7 +2,7 @@ import moment, { DurationInputArg2 } from 'moment' import { hyperionConfig } from '../../config' import { coreUtil, timeUtil } from '../../utils' -import { hyperionStateModel } from '../../models' +import { hyperionStateModel, defaultModel } from '../../models' import updaters from './updaters' @@ -10,6 +10,7 @@ interface GetActionsParams { after: string before: string skip: number + updater: Omit } interface GetActionsResponse { @@ -81,9 +82,9 @@ const getActions = async ( { params: { ...params, - account: 'eosio.evm', // TODO: get it from updater using the notified_account field + account: params.updater.notified_account, limit, - filter: updaters.map(updater => updater.type).join(','), + filter: params.updater.type, sort: 'asc', simple: true, checkLib: true @@ -107,17 +108,11 @@ const getActions = async ( } } -const runUpdaters = async (actions: any[]) => { - for (let index = 0; index < actions.length; index++) { - const action = actions[index] - const updater = updaters.find(item => - item.type.startsWith(`${action.contract}:${action.action}`) - ) - - if (!updater) { - continue - } - +const runUpdater = async ( + updater: Omit, + actions: any[] +) => { + for (const action of actions) { await updater.apply(action) } } @@ -143,10 +138,22 @@ const sync = async (): Promise => { } try { - while (hasMore) { - ;({ hasMore, actions } = await getActions({ after, before, skip })) - skip += actions.length - await runUpdaters(actions) + for (const updater of updaters) { + while (hasMore) { + ;({ hasMore, actions } = await getActions({ + after, + before, + skip, + updater + })) + skip += actions.length + + await runUpdater(updater, actions) + } + + skip = 0 + hasMore = true + actions = [] } } catch (error: any) { console.error('hyperion error', error.message) diff --git a/hapi-evm/src/services/hyperion/updaters/eosio-evm-transfer.updater.ts b/hapi-evm/src/services/hyperion/updaters/eosio-evm-transfer.updater.ts index 2cfe96f24..c7cb103dc 100644 --- a/hapi-evm/src/services/hyperion/updaters/eosio-evm-transfer.updater.ts +++ b/hapi-evm/src/services/hyperion/updaters/eosio-evm-transfer.updater.ts @@ -1,32 +1,74 @@ -import { transferModel } from '../../../models' - -// TODO: handle this as a network function, for example, base on the -// network config, the action type and logic will be different +import { networkConfig } from '../../../config' +import { transferModel, defaultModel } from '../../../models' // TODO: move this to env const defaultMemo = 'Withdraw balance for' -export default { - type: `eosio.evm:withdraw`, - notified_account: `eosio.evm`, - apply: async (action: any) => { - const [amount, symbol] = action.data.quantity.split(' ') - - try { - await transferModel.queries.save({ - block: action.block, - transaction_id: action.transaction_id, - timestamp: action.timestamp, - from: 'eosio.evm', - to: action.data.to, - amount: amount, - symbol: symbol, - memo: `${defaultMemo}: ${action.data.to}`, - quantity: action.data.quantity, - type: transferModel.interfaces.Type.outgoing - }) - } catch (error: any) { - console.error(`error to sync ${action.action}: ${error.message}`) +const applyEosio = async (action: any) => { + if (action.data.from !== 'eosio.evm') return + + try { + await transferModel.queries.save({ + block: action.block, + transaction_id: action.transaction_id, + timestamp: action.timestamp, + from: action.data.from, + to: action.data.to, + amount: action.data.amount, + symbol: action.data.symbol, + memo: action.data.memo, + quantity: action.data.quantity, + type: transferModel.interfaces.Type.outgoing + }) + } catch (error: any) { + console.error(`error to sync ${action.action}: ${error.message}`) + } +} + +const applyTelos = async (action: any) => { + const [amount, symbol] = action.data.quantity.split(' ') + + try { + await transferModel.queries.save({ + block: action.block, + transaction_id: action.transaction_id, + timestamp: action.timestamp, + from: 'eosio.evm', + to: action.data.to, + amount: amount, + symbol: symbol, + memo: `${defaultMemo}: ${action.data.to}`, + quantity: action.data.quantity, + type: transferModel.interfaces.Type.outgoing + }) + } catch (error: any) { + console.error(`error to sync ${action.action}: ${error.message}`) + } +} + +const build = ( + network: defaultModel.AllowedNetworkType = defaultModel.AllowedNetwork.EOSIO +): defaultModel.BuilderListener => { + const listener = { + [defaultModel.AllowedNetwork.EOSIO]: { + type: `eosio.token:transfer,act.data.from=eosio.evm`, + notified_account: `eosio.evm`, + apply: applyEosio + }, + [defaultModel.AllowedNetwork.TELOS]: { + type: `eosio.evm:withdraw`, + notified_account: `eosio.evm`, + apply: applyTelos } } + + const selectedNetworkListener = listener[network] + + if (!selectedNetworkListener) { + throw new Error(`network ${network} not supported`) + } + + return selectedNetworkListener } + +export default build(networkConfig.network)