Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Bitcoin indexing #215

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ INCIDENT_TIME_MINUTES=45
WARNING_TIME_MINUTES=15
INCIDENT_TEMPLATE_PATH="incidentTemplate.ejs"
WARNING_TEMPLATE_PATH="warningTemplate.ejs"
CACHE_TTL_IN_MINS=
CACHE_TTL_IN_MINS=
BTC_PORT=443
BTC_USER=
BTC_PASS=
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ WARNING_TIME_MINUTES=15
INCIDENT_TEMPLATE_PATH="incidentTemplate.ejs"
WARNING_TEMPLATE_PATH="warningTemplate.ejs"
CACHE_TTL_IN_MINS=5
BTC_PORT=443
BTC_USER=
BTC_PASS=
```

### Running locally
Expand Down
13 changes: 11 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ services:
- "9933:9933"
logging:
driver: none
btc:
image: ghcr.io/sygmaprotocol/beacon-api-stub:latest
volumes:
- "./tests/e2e/stubs:/stubs"
ports:
- 8882:8882 # STUB
environment:
- STUB_DATA=/stubs/bitcoin.yml

mongo1:
image: mongo:6
Expand Down Expand Up @@ -81,10 +89,11 @@ services:
restart: always
environment:
DATABASE_URL: mongodb://mongo1:30001/sygmaprotocol-explorer-indexer?replicaSet=my-replica-set&authSource=admin&retryWrites=true&w=majority
SHARED_CONFIG_URL: https://ipfs.io/ipfs/QmW9RhxFFotHtvsU2PMVYU2NNZigeCpbg1ywDCZ3vX675b
RPC_URL_CONFIG: '[{"id": 1, "endpoint": "http://evm1:8545"}, {"id": 2, "endpoint": "http://evm2:8545"}, {"id": 3, "endpoint": "ws://substrate-pallet:9944"}]'
SHARED_CONFIG_URL: https://ipfs.io/ipfs/QmTsYtdvWFf5AwTUAjfo8zHWeURZUMT5q9zEEGjQew5EcB
RPC_URL_CONFIG: '[{"id": 1, "endpoint": "http://evm1:8545"}, {"id": 2, "endpoint": "http://evm2:8545"}, {"id": 3, "endpoint": "ws://substrate-pallet:9944"}, {"id": 4, "endpoint": "http://btc"}]'
RETRY_COUNT: '1'
BACKOFF: '100'
BTC_PORT: 8882

api:
build:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"morgan": "^1.10.0",
"node-cache": "^5.1.2",
"node-cleanup": "^2.1.2",
"rpc-bitcoin": "^2.0.0",
"winston": "^3.3.3",
"winston-transport": "^4.5.0"
},
Expand Down
21 changes: 13 additions & 8 deletions src/indexer/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,24 @@ export type SharedConfig = {
export enum DomainTypes {
EVM = "evm",
SUBSTRATE = "substrate",
BTC = "btc",
}

export type Resource = EvmResource | SubstrateResource | BitcoinResource

export type Domain = {
id: number
name: string
type: DomainTypes
bridge: string
feeAddress?: string
feeRouter: string
feeHandlers: Array<FeeHandlerType>
handlers: Array<Handler>
nativeTokenSymbol: string
nativeTokenDecimals: number
startBlock: number
resources: Array<EvmResource | SubstrateResource>
resources: Array<Resource>
}
type Handler = {
type: ResourceTypes
Expand All @@ -49,24 +53,25 @@ type FeeHandlerType = {
address: string
}

export type EvmResource = {
export type BaseResource = {
resourceId: string
type: ResourceTypes
address: string
symbol: string
decimals: number
}

export type SubstrateResource = {
resourceId: string
type: ResourceTypes
address: string
symbol: string
decimals: number
export type EvmResource = BaseResource

export type SubstrateResource = BaseResource & {
assetName: string
xcmMultiAssetId: XcmAssetId
}

export type BitcoinResource = BaseResource & {
feeAmount: string
}

export type RpcUrlConfig = Array<{
id: number
endpoint: string
Expand Down
122 changes: 82 additions & 40 deletions src/indexer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FastifyInstance } from "fastify"
import { PrismaClient } from "@prisma/client"
import { CronJob } from "cron"
import { caching } from "cache-manager"
import { RPCClient } from "rpc-bitcoin"
import { logger } from "../utils/logger"
import { SubstrateIndexer } from "./services/substrateIndexer/substrateIndexer"
import { EvmIndexer } from "./services/evmIndexer/evmIndexer"
Expand All @@ -23,6 +24,7 @@ import AccountRepository from "./repository/account"
import CoinMarketCapService from "./services/coinmarketcap/coinmarketcap.service"
import { checkTransferStatus, getCronJob } from "./services/monitoringService"
import { NotificationSender } from "./services/monitoringService/notificationSender"
import { BitcoinIndexer } from "./services/bitcoinIndexer/bitcoinIndexer"

interface DomainIndexer {
listenToEvents(): Promise<void>
Expand Down Expand Up @@ -109,57 +111,97 @@ async function init(): Promise<{ domainIndexers: Array<DomainIndexer>; app: Fast
const cronTime = process.env.CRON_TIME || "* */10 * * * *"
const cron = getCronJob(cronTime, checkTransferStatus, transferRepository, notificationSender)
cron.start()

for (const domain of domainsToIndex) {
const rpcURL = rpcUrlConfig.get(domain.id)
if (!rpcURL) {
logger.error(`Local domain is not defined for the domain: ${domain.id}`)
continue
}

if (domain.type == DomainTypes.SUBSTRATE) {
try {
const substrateIndexer = new SubstrateIndexer(
domainRepository,
domain,
executionRepository,
depositRepository,
transferRepository,
feeRepository,
resourceMap,
accountRepository,
coinMarketCapServiceInstance,
sharedConfig,
)
await substrateIndexer.init(rpcURL)
domainIndexers.push(substrateIndexer)
} catch (err) {
logger.error(`Error on domain: ${domain.id}... skipping`)
const blockDelay = Number(process.env[`${domain.id}_BLOCK_DELAY`])
const blockTime = Number(process.env[`${domain.id}_BLOCK_TIME`])

switch (domain.type) {
case DomainTypes.SUBSTRATE: {
try {
const substrateIndexer = new SubstrateIndexer(
domainRepository,
domain,
executionRepository,
depositRepository,
transferRepository,
feeRepository,
resourceMap,
accountRepository,
coinMarketCapServiceInstance,
sharedConfig,
blockDelay || 10,
blockTime || 12000,
)
await substrateIndexer.init(rpcURL)
domainIndexers.push(substrateIndexer)
} catch (err) {
logger.error(`Error on domain: ${domain.id}... skipping`, err)
}
break
}
case DomainTypes.EVM: {
try {
const evmIndexer = new EvmIndexer(
domain,
rpcURL,
domainsToIndex,
domainRepository,
depositRepository,
transferRepository,
executionRepository,
feeRepository,
ofacComplianceService,
accountRepository,
coinMarketCapServiceInstance,
sharedConfig,
blockDelay || 10,
blockTime || 15000,
)
domainIndexers.push(evmIndexer)
} catch (err) {
logger.error(`Error on domain: ${domain.id}... skipping`, err)
}
break
}
} else if (domain.type == DomainTypes.EVM) {
try {
const evmIndexer = new EvmIndexer(
domain,
rpcURL,
domainsToIndex,
domainRepository,
depositRepository,
transferRepository,
executionRepository,
feeRepository,
ofacComplianceService,
accountRepository,
coinMarketCapServiceInstance,
sharedConfig,
)
domainIndexers.push(evmIndexer)
} catch (err) {
logger.error(`Error on domain: ${domain.id}... skipping`)
case DomainTypes.BTC: {
try {
const url = rpcURL
const port = Number(process.env.BTC_PORT) || 443
const user = process.env.BTC_USER
const pass = process.env.BTC_PASS!

const client = new RPCClient({ url, port, user, pass })

const bitcoinIndexer = new BitcoinIndexer(
domainRepository,
domain,
executionRepository,
depositRepository,
transferRepository,
feeRepository,
coinMarketCapServiceInstance,
client,
blockDelay || 3,
blockTime || 12000,
)
domainIndexers.push(bitcoinIndexer)
} catch (err) {
logger.error(`Error on domain: ${domain.id}... skipping`, err)
}
break
}
default: {
logger.error(`Unsupported type: ${JSON.stringify(domain)}`)
}
} else {
logger.error(`Unsupported type: ${JSON.stringify(domain)}`)
}
}

return { domainIndexers, app, cron }
}

Expand Down
Loading
Loading