@hugomrdias/foxer 0.0.1
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.
- package/README.md +56 -0
- package/dist/src/api/index.d.ts +2 -0
- package/dist/src/api/index.d.ts.map +1 -0
- package/dist/src/api/index.js +2 -0
- package/dist/src/api/index.js.map +1 -0
- package/dist/src/api/runner.d.ts +12 -0
- package/dist/src/api/runner.d.ts.map +1 -0
- package/dist/src/api/runner.js +29 -0
- package/dist/src/api/runner.js.map +1 -0
- package/dist/src/api/server.d.ts +18 -0
- package/dist/src/api/server.d.ts.map +1 -0
- package/dist/src/api/server.js +21 -0
- package/dist/src/api/server.js.map +1 -0
- package/dist/src/api/sql-middleware.d.ts +7 -0
- package/dist/src/api/sql-middleware.d.ts.map +1 -0
- package/dist/src/api/sql-middleware.js +95 -0
- package/dist/src/api/sql-middleware.js.map +1 -0
- package/dist/src/api/sql.d.ts +14 -0
- package/dist/src/api/sql.d.ts.map +1 -0
- package/dist/src/api/sql.js +108 -0
- package/dist/src/api/sql.js.map +1 -0
- package/dist/src/api/sse.d.ts +3 -0
- package/dist/src/api/sse.d.ts.map +1 -0
- package/dist/src/api/sse.js +11 -0
- package/dist/src/api/sse.js.map +1 -0
- package/dist/src/bin/dev.d.ts +3 -0
- package/dist/src/bin/dev.d.ts.map +1 -0
- package/dist/src/bin/dev.js +76 -0
- package/dist/src/bin/dev.js.map +1 -0
- package/dist/src/bin/flags.d.ts +32 -0
- package/dist/src/bin/flags.d.ts.map +1 -0
- package/dist/src/bin/flags.js +55 -0
- package/dist/src/bin/flags.js.map +1 -0
- package/dist/src/bin/index.d.ts +3 -0
- package/dist/src/bin/index.d.ts.map +1 -0
- package/dist/src/bin/index.js +22 -0
- package/dist/src/bin/index.js.map +1 -0
- package/dist/src/bin/utils.d.ts +4 -0
- package/dist/src/bin/utils.d.ts.map +1 -0
- package/dist/src/bin/utils.js +52 -0
- package/dist/src/bin/utils.js.map +1 -0
- package/dist/src/client/index.d.ts +18 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +150 -0
- package/dist/src/client/index.js.map +1 -0
- package/dist/src/config/config.d.ts +157 -0
- package/dist/src/config/config.d.ts.map +1 -0
- package/dist/src/config/config.js +65 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/config/env.d.ts +25 -0
- package/dist/src/config/env.d.ts.map +1 -0
- package/dist/src/config/env.js +21 -0
- package/dist/src/config/env.js.map +1 -0
- package/dist/src/contants.d.ts +4 -0
- package/dist/src/contants.d.ts.map +1 -0
- package/dist/src/contants.js +4 -0
- package/dist/src/contants.js.map +1 -0
- package/dist/src/db/actions/blocks.d.ts +44 -0
- package/dist/src/db/actions/blocks.d.ts.map +1 -0
- package/dist/src/db/actions/blocks.js +152 -0
- package/dist/src/db/actions/blocks.js.map +1 -0
- package/dist/src/db/actions/index.d.ts +2 -0
- package/dist/src/db/actions/index.d.ts.map +1 -0
- package/dist/src/db/actions/index.js +2 -0
- package/dist/src/db/actions/index.js.map +1 -0
- package/dist/src/db/actions/transactions.d.ts +11 -0
- package/dist/src/db/actions/transactions.d.ts.map +1 -0
- package/dist/src/db/actions/transactions.js +22 -0
- package/dist/src/db/actions/transactions.js.map +1 -0
- package/dist/src/db/client.d.ts +329 -0
- package/dist/src/db/client.d.ts.map +1 -0
- package/dist/src/db/client.js +108 -0
- package/dist/src/db/client.js.map +1 -0
- package/dist/src/db/column-types.d.ts +132 -0
- package/dist/src/db/column-types.d.ts.map +1 -0
- package/dist/src/db/column-types.js +86 -0
- package/dist/src/db/column-types.js.map +1 -0
- package/dist/src/db/encode.d.ts +10 -0
- package/dist/src/db/encode.d.ts.map +1 -0
- package/dist/src/db/encode.js +79 -0
- package/dist/src/db/encode.js.map +1 -0
- package/dist/src/db/migrate.d.ts +31 -0
- package/dist/src/db/migrate.d.ts.map +1 -0
- package/dist/src/db/migrate.js +147 -0
- package/dist/src/db/migrate.js.map +1 -0
- package/dist/src/db/schema/blocks.d.ts +369 -0
- package/dist/src/db/schema/blocks.d.ts.map +1 -0
- package/dist/src/db/schema/blocks.js +24 -0
- package/dist/src/db/schema/blocks.js.map +1 -0
- package/dist/src/db/schema/index.d.ts +1415 -0
- package/dist/src/db/schema/index.d.ts.map +1 -0
- package/dist/src/db/schema/index.js +18 -0
- package/dist/src/db/schema/index.js.map +1 -0
- package/dist/src/db/schema/transactions.d.ts +336 -0
- package/dist/src/db/schema/transactions.d.ts.map +1 -0
- package/dist/src/db/schema/transactions.js +33 -0
- package/dist/src/db/schema/transactions.js.map +1 -0
- package/dist/src/db/transaction.d.ts +7 -0
- package/dist/src/db/transaction.d.ts.map +1 -0
- package/dist/src/db/transaction.js +8 -0
- package/dist/src/db/transaction.js.map +1 -0
- package/dist/src/hooks/default-hooks.d.ts +2 -0
- package/dist/src/hooks/default-hooks.d.ts.map +1 -0
- package/dist/src/hooks/default-hooks.js +107 -0
- package/dist/src/hooks/default-hooks.js.map +1 -0
- package/dist/src/hooks/registry.d.ts +51 -0
- package/dist/src/hooks/registry.d.ts.map +1 -0
- package/dist/src/hooks/registry.js +32 -0
- package/dist/src/hooks/registry.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/indexer/backfill.d.ts +15 -0
- package/dist/src/indexer/backfill.d.ts.map +1 -0
- package/dist/src/indexer/backfill.js +95 -0
- package/dist/src/indexer/backfill.js.map +1 -0
- package/dist/src/indexer/live.d.ts +20 -0
- package/dist/src/indexer/live.d.ts.map +1 -0
- package/dist/src/indexer/live.js +51 -0
- package/dist/src/indexer/live.js.map +1 -0
- package/dist/src/indexer/process-block.d.ts +29 -0
- package/dist/src/indexer/process-block.d.ts.map +1 -0
- package/dist/src/indexer/process-block.js +91 -0
- package/dist/src/indexer/process-block.js.map +1 -0
- package/dist/src/indexer/queue-block.d.ts +18 -0
- package/dist/src/indexer/queue-block.d.ts.map +1 -0
- package/dist/src/indexer/queue-block.js +38 -0
- package/dist/src/indexer/queue-block.js.map +1 -0
- package/dist/src/indexer/reorg.d.ts +24 -0
- package/dist/src/indexer/reorg.d.ts.map +1 -0
- package/dist/src/indexer/reorg.js +83 -0
- package/dist/src/indexer/reorg.js.map +1 -0
- package/dist/src/indexer/runner.d.ts +14 -0
- package/dist/src/indexer/runner.d.ts.map +1 -0
- package/dist/src/indexer/runner.js +22 -0
- package/dist/src/indexer/runner.js.map +1 -0
- package/dist/src/rpc/client.d.ts +11 -0
- package/dist/src/rpc/client.d.ts.map +1 -0
- package/dist/src/rpc/client.js +18 -0
- package/dist/src/rpc/client.js.map +1 -0
- package/dist/src/rpc/get-block.d.ts +16 -0
- package/dist/src/rpc/get-block.d.ts.map +1 -0
- package/dist/src/rpc/get-block.js +77 -0
- package/dist/src/rpc/get-block.js.map +1 -0
- package/dist/src/rpc/get-logs.d.ts +11 -0
- package/dist/src/rpc/get-logs.d.ts.map +1 -0
- package/dist/src/rpc/get-logs.js +23 -0
- package/dist/src/rpc/get-logs.js.map +1 -0
- package/dist/src/schema.d.ts +3 -0
- package/dist/src/schema.d.ts.map +1 -0
- package/dist/src/schema.js +9 -0
- package/dist/src/schema.js.map +1 -0
- package/dist/src/types.d.ts +22 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +1 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/bloom.d.ts +6 -0
- package/dist/src/utils/bloom.d.ts.map +1 -0
- package/dist/src/utils/bloom.js +30 -0
- package/dist/src/utils/bloom.js.map +1 -0
- package/dist/src/utils/build-conflict-columns.d.ts +4 -0
- package/dist/src/utils/build-conflict-columns.d.ts.map +1 -0
- package/dist/src/utils/build-conflict-columns.js +14 -0
- package/dist/src/utils/build-conflict-columns.js.map +1 -0
- package/dist/src/utils/common.d.ts +2 -0
- package/dist/src/utils/common.d.ts.map +1 -0
- package/dist/src/utils/common.js +4 -0
- package/dist/src/utils/common.js.map +1 -0
- package/dist/src/utils/cursor.d.ts +5 -0
- package/dist/src/utils/cursor.d.ts.map +1 -0
- package/dist/src/utils/cursor.js +8 -0
- package/dist/src/utils/cursor.js.map +1 -0
- package/dist/src/utils/format.d.ts +2 -0
- package/dist/src/utils/format.d.ts.map +1 -0
- package/dist/src/utils/format.js +16 -0
- package/dist/src/utils/format.js.map +1 -0
- package/dist/src/utils/hash.d.ts +9 -0
- package/dist/src/utils/hash.d.ts.map +1 -0
- package/dist/src/utils/hash.js +15 -0
- package/dist/src/utils/hash.js.map +1 -0
- package/dist/src/utils/json.d.ts +5 -0
- package/dist/src/utils/json.d.ts.map +1 -0
- package/dist/src/utils/json.js +11 -0
- package/dist/src/utils/json.js.map +1 -0
- package/dist/src/utils/logger.d.ts +11 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +111 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/shutdown.d.ts +9 -0
- package/dist/src/utils/shutdown.d.ts.map +1 -0
- package/dist/src/utils/shutdown.js +24 -0
- package/dist/src/utils/shutdown.js.map +1 -0
- package/dist/src/utils/timer.d.ts +6 -0
- package/dist/src/utils/timer.d.ts.map +1 -0
- package/dist/src/utils/timer.js +9 -0
- package/dist/src/utils/timer.js.map +1 -0
- package/dist/src/utils/types.d.ts +39 -0
- package/dist/src/utils/types.d.ts.map +1 -0
- package/dist/src/utils/types.js +1 -0
- package/dist/src/utils/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/hello/apps/foc-api/README.md +69 -0
- package/hello/apps/foc-api/biome.json +8 -0
- package/hello/apps/foc-api/index.html +13 -0
- package/hello/apps/foc-api/package.json +39 -0
- package/hello/apps/foc-api/public/vite.svg +1 -0
- package/hello/apps/foc-api/src/app.css +45 -0
- package/hello/apps/foc-api/src/app.tsx +43 -0
- package/hello/apps/foc-api/src/assets/Cloudflare_Logo.svg +51 -0
- package/hello/apps/foc-api/src/assets/react.svg +1 -0
- package/hello/apps/foc-api/src/client.ts +41 -0
- package/hello/apps/foc-api/src/components/account.tsx +100 -0
- package/hello/apps/foc-api/src/components/wallet-options.tsx +43 -0
- package/hello/apps/foc-api/src/index.css +68 -0
- package/hello/apps/foc-api/src/main.tsx +38 -0
- package/hello/apps/foc-api/src/vite-env.d.ts +1 -0
- package/hello/apps/foc-api/tsconfig.app.json +44 -0
- package/hello/apps/foc-api/tsconfig.json +17 -0
- package/hello/apps/foc-api/tsconfig.node.json +25 -0
- package/hello/apps/foc-api/tsconfig.worker.json +8 -0
- package/hello/apps/foc-api/vite.config.ts +8 -0
- package/hello/apps/foc-api/worker/capabilities.ts +25 -0
- package/hello/apps/foc-api/worker/index.ts +64 -0
- package/hello/apps/foc-api/worker/router.ts +35 -0
- package/hello/apps/foc-api/worker-configuration.d.ts +7357 -0
- package/hello/apps/foc-api/wrangler.jsonc +50 -0
- package/hello/apps/foc-app/README.md +69 -0
- package/hello/apps/foc-app/biome.json +8 -0
- package/hello/apps/foc-app/index.html +13 -0
- package/hello/apps/foc-app/package.json +39 -0
- package/hello/apps/foc-app/public/vite.svg +1 -0
- package/hello/apps/foc-app/src/app.css +45 -0
- package/hello/apps/foc-app/src/app.tsx +43 -0
- package/hello/apps/foc-app/src/assets/Cloudflare_Logo.svg +51 -0
- package/hello/apps/foc-app/src/assets/react.svg +1 -0
- package/hello/apps/foc-app/src/client.ts +41 -0
- package/hello/apps/foc-app/src/components/account.tsx +100 -0
- package/hello/apps/foc-app/src/components/wallet-options.tsx +43 -0
- package/hello/apps/foc-app/src/index.css +68 -0
- package/hello/apps/foc-app/src/main.tsx +38 -0
- package/hello/apps/foc-app/src/vite-env.d.ts +1 -0
- package/hello/apps/foc-app/tsconfig.app.json +44 -0
- package/hello/apps/foc-app/tsconfig.json +17 -0
- package/hello/apps/foc-app/tsconfig.node.json +25 -0
- package/hello/apps/foc-app/tsconfig.worker.json +8 -0
- package/hello/apps/foc-app/vite.config.ts +8 -0
- package/hello/apps/foc-app/worker/capabilities.ts +25 -0
- package/hello/apps/foc-app/worker/index.ts +64 -0
- package/hello/apps/foc-app/worker/router.ts +35 -0
- package/hello/apps/foc-app/worker-configuration.d.ts +7357 -0
- package/hello/apps/foc-app/wrangler.jsonc +50 -0
- package/hello/biome.json +50 -0
- package/hello/package.json +22 -0
- package/hello/pnpm-workspace.yaml +3 -0
- package/hello/tsconfig.json +37 -0
- package/package.json +78 -0
- package/src/api/index.ts +1 -0
- package/src/api/runner.ts +43 -0
- package/src/api/server.ts +38 -0
- package/src/api/sql-middleware.ts +131 -0
- package/src/api/sql.ts +149 -0
- package/src/api/sse.ts +12 -0
- package/src/bin/create.ts +199 -0
- package/src/bin/dev.ts +91 -0
- package/src/bin/flags.ts +65 -0
- package/src/bin/index.ts +28 -0
- package/src/bin/utils.ts +55 -0
- package/src/config/config.ts +221 -0
- package/src/config/env.ts +28 -0
- package/src/contants.ts +3 -0
- package/src/db/actions/blocks.ts +209 -0
- package/src/db/actions/index.ts +1 -0
- package/src/db/actions/transactions.ts +32 -0
- package/src/db/client.ts +186 -0
- package/src/db/column-types.ts +105 -0
- package/src/db/encode.ts +99 -0
- package/src/db/migrate.ts +222 -0
- package/src/db/schema/blocks.ts +24 -0
- package/src/db/schema/index.ts +21 -0
- package/src/db/schema/transactions.ts +39 -0
- package/src/db/transaction.ts +20 -0
- package/src/hooks/registry.ts +107 -0
- package/src/index.ts +9 -0
- package/src/indexer/backfill.ts +133 -0
- package/src/indexer/live.ts +76 -0
- package/src/indexer/process-block.ts +142 -0
- package/src/indexer/queue-block.ts +74 -0
- package/src/indexer/reorg.ts +120 -0
- package/src/indexer/runner.ts +35 -0
- package/src/rpc/client.ts +27 -0
- package/src/rpc/get-block.ts +100 -0
- package/src/rpc/get-logs.ts +38 -0
- package/src/schema.ts +10 -0
- package/src/types.ts +32 -0
- package/src/utils/bloom.ts +41 -0
- package/src/utils/build-conflict-columns.ts +26 -0
- package/src/utils/common.ts +3 -0
- package/src/utils/cursor.ts +7 -0
- package/src/utils/format.ts +18 -0
- package/src/utils/hash.ts +17 -0
- package/src/utils/json.ts +11 -0
- package/src/utils/logger.ts +149 -0
- package/src/utils/shutdown.ts +36 -0
- package/src/utils/timer.ts +8 -0
- package/src/utils/types.ts +87 -0
- package/template/biome.json +50 -0
- package/template/package.json +22 -0
- package/template/pnpm-workspace.yaml +3 -0
- package/template/tsconfig.json +37 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/** biome-ignore-all lint/style/noNonNullAssertion: its ok */
|
|
2
|
+
|
|
3
|
+
import { gte } from 'drizzle-orm'
|
|
4
|
+
import {
|
|
5
|
+
getTableConfig,
|
|
6
|
+
type PgAsyncTransaction,
|
|
7
|
+
type PgColumn,
|
|
8
|
+
type PgQueryResultHKT,
|
|
9
|
+
type PgTable,
|
|
10
|
+
} from 'drizzle-orm/pg-core'
|
|
11
|
+
import type { PublicClient } from 'viem'
|
|
12
|
+
import type { FilteredContracts } from '../../config/config.ts'
|
|
13
|
+
import { MAX_QUERY_PARAMS } from '../../contants.ts'
|
|
14
|
+
import { safeGetBlock } from '../../rpc/get-block.ts'
|
|
15
|
+
import type {
|
|
16
|
+
EncodedBlockWithTransactions,
|
|
17
|
+
EncodedTransaction,
|
|
18
|
+
} from '../../types.ts'
|
|
19
|
+
import type { Logger } from '../../utils/logger.ts'
|
|
20
|
+
import { startClock } from '../../utils/timer.ts'
|
|
21
|
+
import type { Database } from '../client.ts'
|
|
22
|
+
import { type relations, schema } from '../schema/index.ts'
|
|
23
|
+
import { insertTransactionsInChunks } from './transactions.ts'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Deletes canonical block rows from a specific block onward.
|
|
27
|
+
*/
|
|
28
|
+
export async function deleteBlocksFrom(
|
|
29
|
+
db: Database,
|
|
30
|
+
fromBlock: bigint
|
|
31
|
+
): Promise<void> {
|
|
32
|
+
const deleteTargets = getTablesWithBlockNumberColumn(
|
|
33
|
+
db._.fullSchema as Record<string, unknown>
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
await db.transaction(async (tx) => {
|
|
37
|
+
for (const target of deleteTargets) {
|
|
38
|
+
await tx
|
|
39
|
+
.delete(target.table)
|
|
40
|
+
.where(gte(target.blockNumberColumn, fromBlock))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// blocks uses `number` instead of blockNumber
|
|
44
|
+
await tx.delete(schema.blocks).where(gte(schema.blocks.number, fromBlock))
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getTablesWithBlockNumberColumn(fullSchema: Record<string, unknown>) {
|
|
49
|
+
const targets: Array<{ table: PgTable; blockNumberColumn: PgColumn }> = []
|
|
50
|
+
|
|
51
|
+
for (const table of Object.values(fullSchema)) {
|
|
52
|
+
const pgTable = table as PgTable
|
|
53
|
+
const config = getTableConfig(pgTable)
|
|
54
|
+
const blockNumberColumn = config.columns.find((column) =>
|
|
55
|
+
['blockNumber', 'block_number'].includes(column.name)
|
|
56
|
+
)
|
|
57
|
+
if (!blockNumberColumn) continue
|
|
58
|
+
|
|
59
|
+
targets.push({ table: pgTable, blockNumberColumn })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return targets
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Caches a block and its transactions in the database.
|
|
67
|
+
*
|
|
68
|
+
* @param args - The arguments for the function
|
|
69
|
+
* @param args.db - The database instance
|
|
70
|
+
* @param args.block - The block to cache
|
|
71
|
+
* @returns The cached block and transactions
|
|
72
|
+
*/
|
|
73
|
+
export async function cacheBlockAndTransactions(args: {
|
|
74
|
+
db: Database<typeof schema, typeof relations>
|
|
75
|
+
block: EncodedBlockWithTransactions
|
|
76
|
+
logger: Logger
|
|
77
|
+
}): Promise<void> {
|
|
78
|
+
const { db, block } = args
|
|
79
|
+
|
|
80
|
+
await db.transaction(async (tx) => {
|
|
81
|
+
await insertBlocksInChunks({
|
|
82
|
+
db: tx,
|
|
83
|
+
blocks: [block],
|
|
84
|
+
})
|
|
85
|
+
await insertTransactionsInChunks({
|
|
86
|
+
db: tx,
|
|
87
|
+
transactions: block.transactions,
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Gets blocks and their transactions from the database in a range and fetches missing blocks from the RPC.
|
|
94
|
+
*
|
|
95
|
+
* @param args - The arguments for the function
|
|
96
|
+
* @param args.logger - The logger instance
|
|
97
|
+
* @param args.db - The database instance
|
|
98
|
+
* @param args.blockNumbers - The block numbers to get
|
|
99
|
+
* @param args.client - The client instance
|
|
100
|
+
* @returns The blocks and their transactions
|
|
101
|
+
*/
|
|
102
|
+
export async function getBlocksInRange(
|
|
103
|
+
logger: Logger,
|
|
104
|
+
db: Database<typeof schema, typeof relations>,
|
|
105
|
+
blockNumbers: bigint[],
|
|
106
|
+
client: PublicClient,
|
|
107
|
+
contracts: FilteredContracts
|
|
108
|
+
): Promise<Map<bigint, EncodedBlockWithTransactions>> {
|
|
109
|
+
const endClock = startClock()
|
|
110
|
+
const firstBlockNumber = blockNumbers[0]!
|
|
111
|
+
const lastBlockNumber = blockNumbers[blockNumbers.length - 1]!
|
|
112
|
+
|
|
113
|
+
// const r = await db.$prepared.getBlocksInRange.execute({
|
|
114
|
+
// firstBlockNumber,
|
|
115
|
+
// lastBlockNumber,
|
|
116
|
+
// contractAddresses: contracts.addresses,
|
|
117
|
+
// })
|
|
118
|
+
|
|
119
|
+
const r = await db.query.blocks.findMany({
|
|
120
|
+
with: {
|
|
121
|
+
transactions: {
|
|
122
|
+
where: {
|
|
123
|
+
AND: [
|
|
124
|
+
{ blockNumber: { gte: firstBlockNumber } },
|
|
125
|
+
{ blockNumber: { lte: lastBlockNumber } },
|
|
126
|
+
{
|
|
127
|
+
to: {
|
|
128
|
+
in: contracts.addresses,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
where: {
|
|
136
|
+
AND: [
|
|
137
|
+
{ number: { gte: firstBlockNumber } },
|
|
138
|
+
{ number: { lte: lastBlockNumber } },
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const blocksByNumber = new Map<bigint, EncodedBlockWithTransactions>()
|
|
144
|
+
const missing = new Set(blockNumbers)
|
|
145
|
+
|
|
146
|
+
for (const block of r) {
|
|
147
|
+
blocksByNumber.set(block.number, block)
|
|
148
|
+
missing.delete(block.number)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const missingBlockNumbers = [...missing]
|
|
152
|
+
const newBlocks: EncodedBlockWithTransactions[] = []
|
|
153
|
+
const newTransactions: EncodedTransaction[] = []
|
|
154
|
+
|
|
155
|
+
await Promise.all(
|
|
156
|
+
missingBlockNumbers.map(async (blockNumber) => {
|
|
157
|
+
const block = await safeGetBlock({ client, blockNumber, db })
|
|
158
|
+
const transactions = block.transactions
|
|
159
|
+
blocksByNumber.set(blockNumber, block)
|
|
160
|
+
newBlocks.push(block)
|
|
161
|
+
if (transactions.length > 0) {
|
|
162
|
+
newTransactions.push(...transactions)
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
await db.transaction(async (tx) => {
|
|
168
|
+
await insertBlocksInChunks({
|
|
169
|
+
db: tx,
|
|
170
|
+
blocks: newBlocks,
|
|
171
|
+
})
|
|
172
|
+
await insertTransactionsInChunks({
|
|
173
|
+
db: tx,
|
|
174
|
+
transactions: newTransactions,
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
logger.info(
|
|
179
|
+
{
|
|
180
|
+
blocks: blocksByNumber.size,
|
|
181
|
+
missing: missingBlockNumbers.length,
|
|
182
|
+
duration: endClock(),
|
|
183
|
+
},
|
|
184
|
+
'get blocks'
|
|
185
|
+
)
|
|
186
|
+
return blocksByNumber
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Inserts blocks in chunks to avoid query parameter limit.
|
|
191
|
+
*/
|
|
192
|
+
export async function insertBlocksInChunks(args: {
|
|
193
|
+
db: PgAsyncTransaction<PgQueryResultHKT, typeof schema>
|
|
194
|
+
blocks: EncodedBlockWithTransactions[]
|
|
195
|
+
}): Promise<void> {
|
|
196
|
+
const { db, blocks } = args
|
|
197
|
+
if (blocks.length === 0) return
|
|
198
|
+
|
|
199
|
+
const batchSize = Math.floor(MAX_QUERY_PARAMS / Object.keys(blocks[0]).length)
|
|
200
|
+
for (let i = 0; i < blocks.length; i += batchSize) {
|
|
201
|
+
const chunk = blocks.slice(i, i + batchSize)
|
|
202
|
+
await db
|
|
203
|
+
.insert(schema.blocks)
|
|
204
|
+
.values(chunk)
|
|
205
|
+
.onConflictDoNothing({
|
|
206
|
+
target: [schema.blocks.number],
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as blocks from './blocks.ts'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PgAsyncTransaction, PgQueryResultHKT } from 'drizzle-orm/pg-core'
|
|
2
|
+
import { MAX_QUERY_PARAMS } from '../../contants.ts'
|
|
3
|
+
import type { EncodedTransaction } from '../../types.ts'
|
|
4
|
+
import { schema } from '../schema/index.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inserts transactions in chunks to avoid query parameter limit.
|
|
8
|
+
*/
|
|
9
|
+
export async function insertTransactionsInChunks(args: {
|
|
10
|
+
db: PgAsyncTransaction<PgQueryResultHKT, typeof schema>
|
|
11
|
+
transactions: EncodedTransaction[]
|
|
12
|
+
}): Promise<void> {
|
|
13
|
+
const { db, transactions } = args
|
|
14
|
+
if (transactions.length === 0) {
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const batchSize = Math.floor(
|
|
19
|
+
MAX_QUERY_PARAMS / Object.keys(transactions[0]).length
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < transactions.length; i += batchSize) {
|
|
23
|
+
const chunk = transactions.slice(i, i + batchSize)
|
|
24
|
+
|
|
25
|
+
await db
|
|
26
|
+
.insert(schema.transactions)
|
|
27
|
+
.values(chunk)
|
|
28
|
+
.onConflictDoNothing({
|
|
29
|
+
target: [schema.transactions.hash],
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/db/client.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { PGlite } from '@electric-sql/pglite'
|
|
2
|
+
import { desc, eq, sql } from 'drizzle-orm'
|
|
3
|
+
import {
|
|
4
|
+
drizzle as drizzleNodePostgres,
|
|
5
|
+
type NodePgDatabase,
|
|
6
|
+
} from 'drizzle-orm/node-postgres'
|
|
7
|
+
import {
|
|
8
|
+
drizzle as drizzlePglite,
|
|
9
|
+
type PgliteDatabase,
|
|
10
|
+
} from 'drizzle-orm/pglite'
|
|
11
|
+
import type { AnyRelations, EmptyRelations } from 'drizzle-orm/relations'
|
|
12
|
+
import { Pool, type PoolConfig } from 'pg'
|
|
13
|
+
import type { DatabaseConfig } from '../config/config.ts'
|
|
14
|
+
import type { Env } from '../config/env.ts'
|
|
15
|
+
import { type relations, schema } from './schema/index.ts'
|
|
16
|
+
|
|
17
|
+
export type Database<
|
|
18
|
+
TSchema extends Record<string, unknown> = Record<string, unknown>,
|
|
19
|
+
TRelations extends AnyRelations = EmptyRelations,
|
|
20
|
+
> =
|
|
21
|
+
| (PgliteDatabase<TSchema, TRelations> & {
|
|
22
|
+
$client: PGlite
|
|
23
|
+
$prepared: ReturnType<typeof generatePrepared>
|
|
24
|
+
})
|
|
25
|
+
| (NodePgDatabase<TSchema, TRelations> & {
|
|
26
|
+
$client: Pool
|
|
27
|
+
$prepared: ReturnType<typeof generatePrepared>
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export type DbDriver = 'pglite' | 'postgres'
|
|
31
|
+
|
|
32
|
+
export type DatabaseContext<
|
|
33
|
+
TSchema extends Record<string, unknown> = Record<string, unknown>,
|
|
34
|
+
TRelations extends AnyRelations = EmptyRelations,
|
|
35
|
+
> =
|
|
36
|
+
| {
|
|
37
|
+
db: NodePgDatabase<TSchema, TRelations> & {
|
|
38
|
+
$client: Pool
|
|
39
|
+
$prepared: ReturnType<typeof generatePrepared>
|
|
40
|
+
}
|
|
41
|
+
driver: 'postgres'
|
|
42
|
+
close: () => Promise<void>
|
|
43
|
+
}
|
|
44
|
+
| {
|
|
45
|
+
db: PgliteDatabase<TSchema, TRelations> & {
|
|
46
|
+
$client: PGlite
|
|
47
|
+
$prepared: ReturnType<typeof generatePrepared>
|
|
48
|
+
}
|
|
49
|
+
driver: 'pglite'
|
|
50
|
+
close: () => Promise<void>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Creates a typed Drizzle database context for either Postgres or PGlite.
|
|
55
|
+
*/
|
|
56
|
+
export function createDatabase<
|
|
57
|
+
TSchema extends Record<string, unknown> = Record<string, unknown>,
|
|
58
|
+
TRelations extends AnyRelations = EmptyRelations,
|
|
59
|
+
>({
|
|
60
|
+
env,
|
|
61
|
+
config,
|
|
62
|
+
schema,
|
|
63
|
+
relations,
|
|
64
|
+
}: {
|
|
65
|
+
env: Env
|
|
66
|
+
config?: DatabaseConfig
|
|
67
|
+
schema: TSchema
|
|
68
|
+
relations: TRelations
|
|
69
|
+
}): DatabaseContext<TSchema, TRelations> {
|
|
70
|
+
let driver: string = 'pglite'
|
|
71
|
+
let url: string | undefined
|
|
72
|
+
let options: PoolConfig | undefined
|
|
73
|
+
|
|
74
|
+
if (env.DATABASE_URL && typeof env.DATABASE_URL === 'string') {
|
|
75
|
+
driver = 'postgres'
|
|
76
|
+
url = env.DATABASE_URL
|
|
77
|
+
} else if (config?.driver === 'postgres') {
|
|
78
|
+
driver = config.driver
|
|
79
|
+
url = config.url
|
|
80
|
+
options = config.options
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (driver === 'postgres' && url) {
|
|
84
|
+
const pool = new Pool({
|
|
85
|
+
...options,
|
|
86
|
+
connectionString: url,
|
|
87
|
+
})
|
|
88
|
+
const db = drizzleNodePostgres({
|
|
89
|
+
client: pool,
|
|
90
|
+
relations: relations,
|
|
91
|
+
schema: schema,
|
|
92
|
+
casing: 'snake_case',
|
|
93
|
+
}) as NodePgDatabase<TSchema, TRelations> & {
|
|
94
|
+
$client: Pool
|
|
95
|
+
$prepared: ReturnType<typeof generatePrepared>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// @ts-expect-error - TODO: fix this
|
|
99
|
+
db.$prepared = generatePrepared(db)
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
db,
|
|
103
|
+
driver: 'postgres',
|
|
104
|
+
close: async () => {
|
|
105
|
+
await pool.end()
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const client = new PGlite(
|
|
111
|
+
config?.driver === 'pglite' && config.directory
|
|
112
|
+
? config.directory
|
|
113
|
+
: '.pglite'
|
|
114
|
+
)
|
|
115
|
+
const db = drizzlePglite({
|
|
116
|
+
client: client,
|
|
117
|
+
relations: relations,
|
|
118
|
+
schema: schema,
|
|
119
|
+
casing: 'snake_case',
|
|
120
|
+
}) as PgliteDatabase<TSchema, TRelations> & {
|
|
121
|
+
$client: PGlite
|
|
122
|
+
$prepared: ReturnType<typeof generatePrepared>
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// @ts-expect-error - TODO: fix this
|
|
126
|
+
db.$prepared = generatePrepared(db)
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
db,
|
|
130
|
+
driver: 'pglite',
|
|
131
|
+
close: async () => {
|
|
132
|
+
await client.close()
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function generatePrepared(
|
|
138
|
+
db: Omit<Database<typeof schema, typeof relations>, '$prepared'>
|
|
139
|
+
) {
|
|
140
|
+
const getLatestBlock = db
|
|
141
|
+
.select({
|
|
142
|
+
number: schema.blocks.number,
|
|
143
|
+
hash: schema.blocks.hash,
|
|
144
|
+
parentHash: schema.blocks.parentHash,
|
|
145
|
+
})
|
|
146
|
+
.from(schema.blocks)
|
|
147
|
+
.orderBy(desc(schema.blocks.number))
|
|
148
|
+
.limit(1)
|
|
149
|
+
.prepare('get_latest_block')
|
|
150
|
+
|
|
151
|
+
const getBlockById = db
|
|
152
|
+
.select({
|
|
153
|
+
number: schema.blocks.number,
|
|
154
|
+
hash: schema.blocks.hash,
|
|
155
|
+
parentHash: schema.blocks.parentHash,
|
|
156
|
+
})
|
|
157
|
+
.from(schema.blocks)
|
|
158
|
+
.where(eq(schema.blocks.number, sql.placeholder('blockNumber')))
|
|
159
|
+
.prepare('get_block_by_id')
|
|
160
|
+
|
|
161
|
+
const getBlocksInRange = db.query.blocks
|
|
162
|
+
.findMany({
|
|
163
|
+
with: {
|
|
164
|
+
transactions: {
|
|
165
|
+
where: {
|
|
166
|
+
to: {
|
|
167
|
+
in: sql.placeholder('contractAddresses'),
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
where: {
|
|
173
|
+
AND: [
|
|
174
|
+
{ number: { gte: sql.placeholder('firstBlockNumber') } },
|
|
175
|
+
{ number: { lte: sql.placeholder('lastBlockNumber') } },
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
})
|
|
179
|
+
.prepare('get_blocks_in_range')
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
getLatestBlock,
|
|
183
|
+
getBlockById,
|
|
184
|
+
getBlocksInRange,
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { customType } from 'drizzle-orm/pg-core'
|
|
2
|
+
import { type Address, type Hash, type Hex, stringify } from 'viem'
|
|
3
|
+
|
|
4
|
+
export const numeric78 = customType<{ data: bigint; driverData: string }>({
|
|
5
|
+
dataType() {
|
|
6
|
+
return 'numeric(78,0)'
|
|
7
|
+
},
|
|
8
|
+
fromDriver(value: string) {
|
|
9
|
+
return BigInt(value)
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
export const hex = customType<{
|
|
14
|
+
data: Hex
|
|
15
|
+
driverData: string
|
|
16
|
+
config?: { length: number | undefined }
|
|
17
|
+
}>({
|
|
18
|
+
dataType(config) {
|
|
19
|
+
if (config?.length) {
|
|
20
|
+
return `varchar(${config.length})`
|
|
21
|
+
}
|
|
22
|
+
return 'varchar'
|
|
23
|
+
},
|
|
24
|
+
fromDriver(value: string) {
|
|
25
|
+
return value as Hex
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Hash column type
|
|
31
|
+
* @param config - Configuration for the column
|
|
32
|
+
* @param config.length - Length of the column (default: 66)
|
|
33
|
+
* @returns Hash column type
|
|
34
|
+
*/
|
|
35
|
+
export const hash = customType<{
|
|
36
|
+
data: Hash
|
|
37
|
+
driverData: string
|
|
38
|
+
config?: { length: number | undefined }
|
|
39
|
+
}>({
|
|
40
|
+
dataType(config) {
|
|
41
|
+
if (config?.length) {
|
|
42
|
+
return `varchar(${config.length})`
|
|
43
|
+
}
|
|
44
|
+
return 'varchar(66)'
|
|
45
|
+
},
|
|
46
|
+
fromDriver(value: string) {
|
|
47
|
+
return value as Hash
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
export const address = customType<{
|
|
52
|
+
data: Address
|
|
53
|
+
driverData: string
|
|
54
|
+
config?: { length: number | undefined }
|
|
55
|
+
}>({
|
|
56
|
+
dataType(config) {
|
|
57
|
+
if (config?.length) {
|
|
58
|
+
return `varchar(${config.length})`
|
|
59
|
+
}
|
|
60
|
+
return 'varchar(42)'
|
|
61
|
+
},
|
|
62
|
+
fromDriver(value: string) {
|
|
63
|
+
return value as Address
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Bigint column type (8 bytes)
|
|
69
|
+
* For bigger number see {@link numeric78}, {@link uint256} and {@link int256}
|
|
70
|
+
* @returns Bigint column type
|
|
71
|
+
*/
|
|
72
|
+
export const bigint = customType<{ data: bigint; driverData: string }>({
|
|
73
|
+
dataType() {
|
|
74
|
+
return 'bigint'
|
|
75
|
+
},
|
|
76
|
+
fromDriver(value: string) {
|
|
77
|
+
return BigInt(value)
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
export const uint256 = numeric78
|
|
82
|
+
export const int256 = numeric78
|
|
83
|
+
|
|
84
|
+
export const jsonb = customType<{ data: unknown; driverData: string }>({
|
|
85
|
+
dataType() {
|
|
86
|
+
return 'jsonb'
|
|
87
|
+
},
|
|
88
|
+
toDriver(value: unknown): string {
|
|
89
|
+
return stringify(value)
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
export const bytea = customType<{ data: Hex; driverData: Buffer }>({
|
|
94
|
+
dataType() {
|
|
95
|
+
return 'bytea'
|
|
96
|
+
},
|
|
97
|
+
toDriver(value: string): Buffer {
|
|
98
|
+
return Buffer.from(value.slice(2), 'hex')
|
|
99
|
+
},
|
|
100
|
+
fromDriver(value: Buffer): Hex {
|
|
101
|
+
const hex = value.toString('hex')
|
|
102
|
+
const _value = hex.startsWith('\\x') ? hex.slice(2) : hex
|
|
103
|
+
return `0x${_value}` as Hex
|
|
104
|
+
},
|
|
105
|
+
})
|
package/src/db/encode.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Hash } from 'viem'
|
|
2
|
+
import type {
|
|
3
|
+
ChainBlock,
|
|
4
|
+
ChainTransaction,
|
|
5
|
+
EncodedBlock,
|
|
6
|
+
EncodedBlockWithTransactions,
|
|
7
|
+
EncodedTransaction,
|
|
8
|
+
} from '../types'
|
|
9
|
+
|
|
10
|
+
export function encodeTransaction(tx: ChainTransaction): EncodedTransaction {
|
|
11
|
+
return {
|
|
12
|
+
hash: tx.hash,
|
|
13
|
+
blockNumber: tx.blockNumber,
|
|
14
|
+
transactionIndex: tx.transactionIndex,
|
|
15
|
+
blockHash: tx.blockHash,
|
|
16
|
+
from: tx.from,
|
|
17
|
+
to: tx.to ?? null,
|
|
18
|
+
input: tx.input,
|
|
19
|
+
value: tx.value,
|
|
20
|
+
nonce: tx.nonce,
|
|
21
|
+
r: tx.r,
|
|
22
|
+
s: tx.s,
|
|
23
|
+
v: tx.v,
|
|
24
|
+
type: tx.type,
|
|
25
|
+
gas: tx.gas ?? null,
|
|
26
|
+
gasPrice: tx.gasPrice ?? null,
|
|
27
|
+
maxFeePerGas: tx.maxFeePerGas ?? null,
|
|
28
|
+
maxPriorityFeePerGas: tx.maxPriorityFeePerGas ?? null,
|
|
29
|
+
accessList: tx.accessList ?? null,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function encodeBlock(block: ChainBlock): EncodedBlock {
|
|
34
|
+
return {
|
|
35
|
+
number: block.number,
|
|
36
|
+
timestamp: block.timestamp,
|
|
37
|
+
hash: block.hash,
|
|
38
|
+
parentHash: block.parentHash,
|
|
39
|
+
logsBloom: block.logsBloom,
|
|
40
|
+
miner: block.miner,
|
|
41
|
+
gasUsed: block.gasUsed,
|
|
42
|
+
gasLimit: block.gasLimit,
|
|
43
|
+
baseFeePerGas: block.baseFeePerGas ?? null,
|
|
44
|
+
nonce: block.nonce,
|
|
45
|
+
mixHash: block.mixHash,
|
|
46
|
+
stateRoot: block.stateRoot,
|
|
47
|
+
receiptsRoot: block.receiptsRoot,
|
|
48
|
+
transactionsRoot: block.transactionsRoot,
|
|
49
|
+
sha3Uncles: block.sha3Uncles,
|
|
50
|
+
size: block.size,
|
|
51
|
+
difficulty: block.difficulty,
|
|
52
|
+
totalDifficulty: block.totalDifficulty ?? null,
|
|
53
|
+
extraData: block.extraData,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function encodeBlockWithTransactions(
|
|
58
|
+
block: ChainBlock
|
|
59
|
+
): EncodedBlockWithTransactions {
|
|
60
|
+
return {
|
|
61
|
+
...encodeBlock(block),
|
|
62
|
+
transactions: block.transactions.map(encodeTransaction),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const EMPTY_TRIE_HASH =
|
|
67
|
+
'0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'
|
|
68
|
+
const EMPTY_LOGS_BLOOM =
|
|
69
|
+
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
|
70
|
+
export function encodeNullRoundBlock(options: {
|
|
71
|
+
number: bigint
|
|
72
|
+
hash: Hash
|
|
73
|
+
}): EncodedBlockWithTransactions {
|
|
74
|
+
return {
|
|
75
|
+
number: options.number,
|
|
76
|
+
// TODO: probably should be previous block timestamp plus 30 seconds
|
|
77
|
+
timestamp: BigInt(Math.floor(Date.now() / 1000)),
|
|
78
|
+
hash: options.hash,
|
|
79
|
+
parentHash: options.hash,
|
|
80
|
+
logsBloom: EMPTY_LOGS_BLOOM,
|
|
81
|
+
miner: '0x0000000000000000000000000000000000000000',
|
|
82
|
+
gasUsed: 0n,
|
|
83
|
+
gasLimit: 30_000_000n,
|
|
84
|
+
baseFeePerGas: 1_000_000_000n,
|
|
85
|
+
nonce: '0x0000000000000000',
|
|
86
|
+
mixHash:
|
|
87
|
+
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
88
|
+
stateRoot: EMPTY_TRIE_HASH,
|
|
89
|
+
receiptsRoot: EMPTY_TRIE_HASH,
|
|
90
|
+
transactionsRoot: EMPTY_TRIE_HASH,
|
|
91
|
+
sha3Uncles:
|
|
92
|
+
'0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
|
|
93
|
+
size: 0n,
|
|
94
|
+
difficulty: 0n,
|
|
95
|
+
totalDifficulty: 0n,
|
|
96
|
+
extraData: '0x',
|
|
97
|
+
transactions: [],
|
|
98
|
+
}
|
|
99
|
+
}
|