@xyo-network/chain-api 1.15.1 → 1.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/node/driver/mongo/MongoMap.d.ts +9 -3
  2. package/dist/node/driver/mongo/MongoMap.d.ts.map +1 -1
  3. package/dist/node/helpers/index.d.ts +5 -0
  4. package/dist/node/helpers/index.d.ts.map +1 -0
  5. package/dist/node/helpers/initChainId.d.ts +4 -0
  6. package/dist/node/helpers/initChainId.d.ts.map +1 -0
  7. package/dist/node/helpers/initEvmProvider.d.ts +11 -0
  8. package/dist/node/helpers/initEvmProvider.d.ts.map +1 -0
  9. package/dist/node/helpers/initInfuraProvider.d.ts +6 -0
  10. package/dist/node/helpers/initInfuraProvider.d.ts.map +1 -0
  11. package/dist/node/helpers/initJsonRpcProvider.d.ts +6 -0
  12. package/dist/node/helpers/initJsonRpcProvider.d.ts.map +1 -0
  13. package/dist/node/index.mjs +197 -42
  14. package/dist/node/index.mjs.map +1 -1
  15. package/dist/node/manifest/getLocator.d.ts.map +1 -1
  16. package/dist/node/server/app.d.ts +2 -1
  17. package/dist/node/server/app.d.ts.map +1 -1
  18. package/dist/node/server/routes/addRoutes.d.ts +2 -1
  19. package/dist/node/server/routes/addRoutes.d.ts.map +1 -1
  20. package/dist/node/server/routes/healthz/get.d.ts +2 -1
  21. package/dist/node/server/routes/healthz/get.d.ts.map +1 -1
  22. package/dist/node/server/routes/rpc/routes/addRpcRoutes.d.ts +2 -1
  23. package/dist/node/server/routes/rpc/routes/addRpcRoutes.d.ts.map +1 -1
  24. package/dist/node/server/server.d.ts.map +1 -1
  25. package/package.json +56 -51
  26. package/src/driver/mongo/MongoMap.ts +42 -4
  27. package/src/helpers/index.ts +4 -0
  28. package/src/helpers/initChainId.ts +20 -0
  29. package/src/helpers/initEvmProvider.ts +24 -0
  30. package/src/helpers/initInfuraProvider.ts +27 -0
  31. package/src/helpers/initJsonRpcProvider.ts +21 -0
  32. package/src/manifest/getLocator.ts +22 -9
  33. package/src/manifest/public/Chain.json +52 -15
  34. package/src/server/app.ts +3 -2
  35. package/src/server/routes/addRoutes.ts +3 -2
  36. package/src/server/routes/healthz/get.ts +1 -1
  37. package/src/server/routes/rpc/routes/addRpcRoutes.ts +6 -5
  38. package/src/server/server.ts +23 -2
@@ -0,0 +1,4 @@
1
+ export * from './initChainId.ts'
2
+ export * from './initEvmProvider.ts'
3
+ export * from './initInfuraProvider.ts'
4
+ export * from './initJsonRpcProvider.ts'
@@ -0,0 +1,20 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import { hexFrom, isHex } from '@xylabs/hex'
3
+ import { isDefined } from '@xylabs/typeof'
4
+ import type { Config } from '@xyo-network/xl1-protocol-sdk'
5
+
6
+ export const canUseChainId = (config: Config): boolean => {
7
+ return isDefined(config.evm.chainId)
8
+ }
9
+
10
+ export const getChainId = (config: Config) => {
11
+ const chainId = assertEx(config.evm.chainId, () => 'Missing config.evm.chainId')
12
+ if (isHex(chainId, { prefix: true })) {
13
+ const hex = hexFrom(chainId)
14
+ const parsed = Number.parseInt(hex, 16)
15
+ return parsed
16
+ } else {
17
+ const parsed = Number.parseInt(chainId, 10)
18
+ return parsed
19
+ }
20
+ }
@@ -0,0 +1,24 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import type { Logger } from '@xylabs/logger'
3
+ import type { Config } from '@xyo-network/xl1-protocol-sdk'
4
+ import type { Provider } from 'ethers'
5
+ import type { JsonRpcProvider } from 'ethers/providers'
6
+
7
+ import { canUseInfuraProvider, initInfuraProvider } from './initInfuraProvider.ts'
8
+ import { canUseJsonRpcProvider, initJsonRpcProvider } from './initJsonRpcProvider.ts'
9
+
10
+ let provider: Promise<JsonRpcProvider> | undefined
11
+
12
+ export const initEvmProvider = async ({ config }: { config: Config; logger?: Logger }): Promise<Provider> => {
13
+ if (provider) return provider
14
+ if (canUseInfuraProvider(config)) {
15
+ provider = initInfuraProvider(config)
16
+ } else if (canUseJsonRpcProvider(config)) {
17
+ provider = initJsonRpcProvider(config)
18
+ }
19
+ return assertEx(await provider, () => 'Error: No provider available')
20
+ }
21
+
22
+ export const canUseEvmProvider = ({ config }: { config: Config }) => {
23
+ return canUseInfuraProvider(config) || canUseJsonRpcProvider(config)
24
+ }
@@ -0,0 +1,27 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import { isDefined } from '@xylabs/typeof'
3
+ import type { Config } from '@xyo-network/xl1-protocol-sdk'
4
+ import { InfuraProvider } from 'ethers/providers'
5
+
6
+ import { canUseChainId, getChainId } from './initChainId.ts'
7
+
8
+ let instance: Promise<InfuraProvider> | undefined
9
+
10
+ export const initInfuraProvider = (config: Config) => {
11
+ if (instance) return instance
12
+ const providerConfig = getInfuraProviderConfig(config)
13
+ instance = Promise.resolve(new InfuraProvider(...providerConfig))
14
+ return instance
15
+ }
16
+
17
+ export const canUseInfuraProvider = (config: Config): boolean => {
18
+ return canUseChainId(config)
19
+ && isDefined(config.evm?.infura?.projectId)
20
+ && isDefined(config.evm?.infura?.projectSecret)
21
+ }
22
+
23
+ export const getInfuraProviderConfig = (config: Config) => {
24
+ const projectId = assertEx(config.evm?.infura?.projectId, () => 'Missing config.evm.infura.projectId')
25
+ const projectSecret = assertEx(config.evm?.infura?.projectSecret, () => 'Missing config.evm.infura.projectSecret')
26
+ return [getChainId(config), projectId, projectSecret] as const
27
+ }
@@ -0,0 +1,21 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import { isDefined } from '@xylabs/typeof'
3
+ import type { Config } from '@xyo-network/xl1-protocol-sdk'
4
+ import { JsonRpcProvider } from 'ethers/providers'
5
+
6
+ import { canUseChainId, getChainId } from './initChainId.ts'
7
+
8
+ export const initJsonRpcProvider = (config: Config) => {
9
+ const providerConfig = getJsonRpcProviderConfig(config)
10
+ return Promise.resolve(new JsonRpcProvider(...providerConfig))
11
+ }
12
+
13
+ export const canUseJsonRpcProvider = (config: Config) => {
14
+ return canUseChainId(config)
15
+ && isDefined(config.evm.jsonRpc?.url)
16
+ }
17
+
18
+ export const getJsonRpcProviderConfig = (config: Config) => {
19
+ const jsonRpcUrl = assertEx(config.evm.jsonRpc?.url, () => 'Missing config.evm.jsonRpc.url')
20
+ return [jsonRpcUrl, getChainId(config)] as const
21
+ }
@@ -8,16 +8,17 @@ import { MemoryArchivist } from '@xyo-network/archivist-memory'
8
8
  import { MongoDBArchivistV2 } from '@xyo-network/archivist-mongodb'
9
9
  import { ViewArchivist } from '@xyo-network/archivist-view'
10
10
  import {
11
- AddressBalanceDivinerV2, ArchivistSyncDiviner, balanceSummaryRepositoryFromMap, HeadValidationDiviner,
11
+ AddressBalanceDivinerV2, AddressTransferDiviner, ArchivistSyncDiviner, HeadValidationDiviner,
12
12
  } from '@xyo-network/chain-modules'
13
- import type { MapType } from '@xyo-network/chain-protocol'
14
13
  import { initTelemetry } from '@xyo-network/chain-telemetry'
15
14
  import { AbstractModule, LoggerModuleStatusReporter } from '@xyo-network/module-abstract'
16
15
  import { ModuleFactoryLocator } from '@xyo-network/module-factory-locator'
17
16
  import type { MongoDBModuleParamsV2 } from '@xyo-network/module-model-mongodb'
18
17
  import type { WithStorageMeta } from '@xyo-network/payload-model'
19
18
  import { MemorySentinel } from '@xyo-network/sentinel-memory'
20
- import type { BalancesStepSummary, Config } from '@xyo-network/xl1-protocol-sdk'
19
+ import type {
20
+ BalancesStepSummary, Config, MapType, TransfersStepSummary,
21
+ } from '@xyo-network/xl1-protocol-sdk'
21
22
  import { hasMongoConfig } from '@xyo-network/xl1-protocol-sdk'
22
23
 
23
24
  import { MongoMap } from '../driver/index.ts'
@@ -51,8 +52,8 @@ export const getLocator = async (context: GetLocatorContext) => {
51
52
  const statusReporter = logger ? new LoggerModuleStatusReporter(logger) : undefined
52
53
 
53
54
  const locator = new ModuleFactoryLocator()
54
- // Initialize with an in-memory map for backing the summary repository
55
- let summaryMap: MapType<Hash, WithStorageMeta<BalancesStepSummary>> = new Map<Hash, WithStorageMeta<BalancesStepSummary>>()
55
+ let balanceSummaryMap: MapType<string, WithStorageMeta<BalancesStepSummary>> | undefined
56
+ let transferSummaryMap: MapType<string, WithStorageMeta<TransfersStepSummary>> | undefined
56
57
  // If there's a MongoDB configuration
57
58
  const mongoConfig = config.storage?.mongo
58
59
  if (hasMongoConfig(mongoConfig)) {
@@ -70,13 +71,25 @@ export const getLocator = async (context: GetLocatorContext) => {
70
71
  locator.register(MongoDBArchivistV2.factory(params), undefined, true)
71
72
 
72
73
  // Use a persistent MongoMap for the summary repository if MongoDB is configured
73
- const sdk = new BaseMongoSdk<WithStorageMeta<BalancesStepSummary>>({ ...payloadSdkConfig, collection: 'balance_summary_map' })
74
- summaryMap = await MongoMap.create<MongoMap<Hash, WithStorageMeta<BalancesStepSummary>>>({ sdk })
74
+ const sdkBalanceSummaryMap = new BaseMongoSdk<WithStorageMeta<BalancesStepSummary>>({ ...payloadSdkConfig, collection: 'balance_summary_map' })
75
+ balanceSummaryMap = await MongoMap.create<MongoMap<Hash, WithStorageMeta<BalancesStepSummary>>>({
76
+ sdk: sdkBalanceSummaryMap,
77
+ getCache: { enabled: true, maxEntries: 5000 },
78
+ })
79
+
80
+ const sdkTransferSummaryMap = new BaseMongoSdk<WithStorageMeta<TransfersStepSummary>>({ ...payloadSdkConfig, collection: 'transfer_summary_map' })
81
+ transferSummaryMap = await MongoMap.create<MongoMap<Hash, WithStorageMeta<TransfersStepSummary>>>({
82
+ sdk: sdkTransferSummaryMap,
83
+ getCache: { enabled: true, maxEntries: 5000 },
84
+ })
75
85
  }
76
86
 
77
- const summaryRepository = balanceSummaryRepositoryFromMap(summaryMap)
78
87
  locator.register(AddressBalanceDivinerV2.factory<AddressBalanceDivinerV2>({
79
- traceProvider, meterProvider, statusReporter, summaryRepository,
88
+ traceProvider, meterProvider, statusReporter, summaryMap: balanceSummaryMap,
89
+ }))
90
+
91
+ locator.register(AddressTransferDiviner.factory<AddressTransferDiviner>({
92
+ traceProvider, meterProvider, statusReporter, summaryMap: transferSummaryMap,
80
93
  }))
81
94
 
82
95
  const chainId = isDefined(config.chain.id)
@@ -30,12 +30,12 @@
30
30
  "eventSubscriptions": [
31
31
  {
32
32
  "sourceEvent": "inserted",
33
- "sourceModule": "Chain:Submissions",
33
+ "sourceModule": "Submissions",
34
34
  "targetModuleFunction": "divine"
35
35
  }
36
36
  ],
37
- "inArchivist": "Chain:Submissions",
38
- "outArchivist": "Chain:Validated",
37
+ "inArchivist": "Submissions",
38
+ "outArchivist": "Validated",
39
39
  "name": "HeadValidationDiviner"
40
40
  }
41
41
  },
@@ -66,7 +66,7 @@
66
66
  "accountPath": "1/1'/4'",
67
67
  "automations": [
68
68
  {
69
- "frequency": 60000,
69
+ "frequency": 10000,
70
70
  "frequencyUnits": "millis",
71
71
  "schema": "network.xyo.automation.interval",
72
72
  "type": "interval"
@@ -82,6 +82,28 @@
82
82
  }
83
83
  ]
84
84
  }
85
+ },
86
+ {
87
+ "config": {
88
+ "accountPath": "1/1'/5'",
89
+ "automations": [
90
+ {
91
+ "frequency": 10000,
92
+ "frequencyUnits": "millis",
93
+ "schema": "network.xyo.automation.interval",
94
+ "type": "interval"
95
+ }
96
+ ],
97
+ "name": "AddressTransferPollingSentinel",
98
+ "schema": "network.xyo.sentinel.config",
99
+ "synchronous": true,
100
+ "tasks": [
101
+ {
102
+ "mod": "AddressTransferDiviner",
103
+ "endPoint": "divine"
104
+ }
105
+ ]
106
+ }
85
107
  }
86
108
  ],
87
109
  "public": [
@@ -111,23 +133,38 @@
111
133
  "enabled": true,
112
134
  "maxEntries": 5000
113
135
  },
114
- "originArchivist": "Chain:Validated",
136
+ "originArchivist": "Validated",
115
137
  "schema": "network.xyo.archivist.view.config"
116
138
  }
117
139
  },
118
140
  {
119
141
  "config": {
120
- "accountPath": "1/1/3",
121
- "schema": "network.xyo.diviner.chain.address.balance.config",
122
- "archivist": "Chain:Validated",
142
+ "accountPath": "1/1/3'",
123
143
  "name": "AddressBalanceDiviner",
124
- "eventSubscriptions": [
125
- {
126
- "sourceEvent": "inserted",
127
- "sourceModule": "Chain:Validated",
128
- "targetModuleFunction": "divine"
144
+ "schema": "network.xyo.diviner.chain.address.balance.config",
145
+ "map": {
146
+ "lmdb": {
147
+ "dbName": "summaries",
148
+ "storeName": "address_balance",
149
+ "location": ".store"
129
150
  }
130
- ]
151
+ },
152
+ "archivist": "Validated"
153
+ }
154
+ },
155
+ {
156
+ "config": {
157
+ "accountPath": "1/1/4'",
158
+ "name": "AddressTransferDiviner",
159
+ "schema": "network.xyo.diviner.chain.address.transfer.config",
160
+ "map": {
161
+ "lmdb": {
162
+ "dbName": "summaries",
163
+ "storeName": "address_transfer",
164
+ "location": ".store"
165
+ }
166
+ },
167
+ "archivist": "Validated"
131
168
  }
132
169
  }
133
170
  ]
@@ -135,4 +172,4 @@
135
172
  }
136
173
  ],
137
174
  "schema": "network.xyo.manifest"
138
- }
175
+ }
package/src/server/app.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  standardResponses,
10
10
  } from '@xylabs/express'
11
11
  import type { NodeInstance } from '@xyo-network/node-model'
12
+ import type { StakedChainContextRead, StakeEventsRead } from '@xyo-network/xl1-protocol-sdk'
12
13
  import compression from 'compression'
13
14
  import cors from 'cors'
14
15
  import type { Express } from 'express'
@@ -17,7 +18,7 @@ import express from 'express'
17
18
  import { addInstrumentation } from './instrumentation.ts'
18
19
  import { addRoutes } from './routes/index.ts'
19
20
 
20
- export const getApp = (node: NodeInstance): Express => {
21
+ export const getApp = (node: NodeInstance, eventsReader: StakeEventsRead, stakedChainContext: StakedChainContextRead): Express => {
21
22
  addInstrumentation()
22
23
  const app = express()
23
24
  app.set('etag', false)
@@ -31,7 +32,7 @@ export const getApp = (node: NodeInstance): Express => {
31
32
  app.use(customPoweredByHeader)
32
33
  disableCaseSensitiveRouting(app)
33
34
  app.node = node
34
- addRoutes(app)
35
+ addRoutes(app, eventsReader, stakedChainContext)
35
36
  app.use(standardErrors)
36
37
  return app
37
38
  }
@@ -1,11 +1,12 @@
1
+ import type { StakedChainContextRead, StakeEventsRead } from '@xyo-network/xl1-protocol-sdk'
1
2
  import type { Express } from 'express'
2
3
 
3
4
  import { addNodeRoutes } from './address/index.ts'
4
5
  import { addDataLakeRoutes } from './dataLake/index.ts'
5
6
  import { addRpcRoutes } from './rpc/index.ts'
6
7
 
7
- export const addRoutes = (app: Express) => {
8
- addRpcRoutes(app)
8
+ export const addRoutes = (app: Express, eventsReader: StakeEventsRead, stakedChainContext: StakedChainContextRead) => {
9
+ addRpcRoutes(app, eventsReader, stakedChainContext)
9
10
  addDataLakeRoutes(app)
10
11
  addNodeRoutes(app)
11
12
  }
@@ -17,4 +17,4 @@ const handler: RequestHandler<NoReqParams> = (_req, res) => {
17
17
  res.status(200).send(data)
18
18
  }
19
19
 
20
- export const getHealthz = handler
20
+ export const getHealthz: RequestHandler<NoReqParams> = handler
@@ -1,17 +1,18 @@
1
1
  import { setRawResponseFormat } from '@xylabs/express'
2
2
  import { NodeXyoViewer } from '@xyo-network/chain-rpc'
3
+ import type { StakedChainContextRead, StakeEventsRead } from '@xyo-network/xl1-protocol-sdk'
3
4
  import {
4
- NodeXyoRunner, rpcEngineFromProvider,
5
+ NodeXyoRunner, rpcEngineFromConnection,
5
6
  XyoBaseConnection,
6
7
  } from '@xyo-network/xl1-rpc'
7
8
  import type { Express } from 'express'
8
9
 
9
- export const addRpcRoutes = (app: Express) => {
10
+ export const addRpcRoutes = (app: Express, eventsReader: StakeEventsRead, stakedChainContext: StakedChainContextRead) => {
10
11
  const { node } = app
11
12
  const runner = new NodeXyoRunner(node)
12
- const viewer = new NodeXyoViewer(node)
13
- const provider = new XyoBaseConnection({ runner, viewer })
14
- const engine = rpcEngineFromProvider(provider)
13
+ const viewer = new NodeXyoViewer(node, eventsReader, stakedChainContext)
14
+ const connection = new XyoBaseConnection({ runner, viewer })
15
+ const engine = rpcEngineFromConnection(connection)
15
16
 
16
17
  app.post('/rpc', (req, res) => {
17
18
  setRawResponseFormat(res)
@@ -1,12 +1,19 @@
1
1
  import { assertEx } from '@xylabs/assert'
2
+ import { toEthAddress, ZERO_HASH } from '@xylabs/hex'
2
3
  import type { Logger } from '@xylabs/logger'
3
4
  import { isDefined, isString } from '@xylabs/typeof'
5
+ import { asArchivistInstance } from '@xyo-network/archivist-model'
4
6
  import { boot } from '@xyo-network/bios'
5
7
  import type { BiosExternalInterface } from '@xyo-network/bios-model'
8
+ import { EthereumChainStake, EthereumChainStakeEvents } from '@xyo-network/chain-ethereum'
6
9
  import type { NodeInstance } from '@xyo-network/node-model'
10
+ import type { Payload, WithStorageMeta } from '@xyo-network/payload-model'
11
+ import { StakedXyoChainV2__factory } from '@xyo-network/typechain'
7
12
  import { HDWallet } from '@xyo-network/wallet'
8
- import { type Config } from '@xyo-network/xl1-protocol-sdk'
13
+ import type { ChainId } from '@xyo-network/xl1-protocol'
14
+ import { type Config, readPayloadMapFromStore } from '@xyo-network/xl1-protocol-sdk'
9
15
 
16
+ import { initInfuraProvider } from '../helpers/index.ts'
10
17
  import { getNode } from '../manifest/index.ts'
11
18
  import { getApp } from './app.ts'
12
19
 
@@ -49,10 +56,24 @@ export const getServer = async (context: GetServerContext) => {
49
56
  const bios = await boot()
50
57
  const seedPhrase = isDefined(mnemonic) ? mnemonic : await getSeedPhrase(bios, config, logger)
51
58
  const wallet = await HDWallet.fromPhrase(seedPhrase)
59
+ const provider = await initInfuraProvider(config)
60
+ const contractAddress = assertEx(config.chain.id, () => 'Missing config.evm.chainId') as ChainId
61
+ const contract = StakedXyoChainV2__factory.connect(toEthAddress(contractAddress), provider)
52
62
  const nodeContext = {
53
63
  wallet, logger, config,
54
64
  }
55
- const app = getApp(node ?? await getNode(nodeContext))
65
+ const eventsReader = new EthereumChainStakeEvents(contract)
66
+ const stakeChainReader = await EthereumChainStake.create({ contract, eventsReader })
67
+ await stakeChainReader.start()
68
+ const rootNode = await getNode(nodeContext)
69
+ const archivist = assertEx(asArchivistInstance(
70
+ await rootNode.resolve('Chain:Validated'),
71
+ { required: true },
72
+ ), () => 'FinalizedArchivist not found in node')
73
+ const chainMap = readPayloadMapFromStore<WithStorageMeta<Payload>>(archivist)
74
+ const app = getApp(node ?? await getNode(nodeContext), eventsReader, {
75
+ chainId: contractAddress, stake: stakeChainReader, store: { chainMap }, head: () => ZERO_HASH,
76
+ })
56
77
  const server = app.listen(port, hostname, () => logger?.log(`[API] Server listening at http://${hostname}:${port}`))
57
78
  server.setTimeout(20_000)
58
79
  return server