@effectstream/sync 0.100.0

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 (45) hide show
  1. package/package.json +29 -0
  2. package/src/mod.ts +3 -0
  3. package/src/sync-protocols/avail/AvailClient.ts +120 -0
  4. package/src/sync-protocols/avail/fetcher.ts +174 -0
  5. package/src/sync-protocols/avail/state.ts +134 -0
  6. package/src/sync-protocols/avail/types.ts +92 -0
  7. package/src/sync-protocols/base/fetcher.ts +43 -0
  8. package/src/sync-protocols/base/page.ts +89 -0
  9. package/src/sync-protocols/base/primitive.ts +24 -0
  10. package/src/sync-protocols/base/state.ts +143 -0
  11. package/src/sync-protocols/bitcoin/fetcher.ts +549 -0
  12. package/src/sync-protocols/bitcoin/state.ts +144 -0
  13. package/src/sync-protocols/bitcoin/types.ts +92 -0
  14. package/src/sync-protocols/celestia/CelestiaClient.ts +139 -0
  15. package/src/sync-protocols/celestia/fetcher.ts +165 -0
  16. package/src/sync-protocols/celestia/state.ts +131 -0
  17. package/src/sync-protocols/celestia/types.ts +46 -0
  18. package/src/sync-protocols/common/page-helpers.ts +65 -0
  19. package/src/sync-protocols/common/root.ts +35 -0
  20. package/src/sync-protocols/common/utils.ts +29 -0
  21. package/src/sync-protocols/evm/fetcher.ts +290 -0
  22. package/src/sync-protocols/evm/state.ts +122 -0
  23. package/src/sync-protocols/evm/types.ts +53 -0
  24. package/src/sync-protocols/midnight/MidnightClient.ts +210 -0
  25. package/src/sync-protocols/midnight/fetcher.ts +252 -0
  26. package/src/sync-protocols/midnight/state.ts +147 -0
  27. package/src/sync-protocols/midnight/types.ts +128 -0
  28. package/src/sync-protocols/midnight/zswap-decoder.ts +123 -0
  29. package/src/sync-protocols/mod.ts +29 -0
  30. package/src/sync-protocols/near/NearClient.ts +178 -0
  31. package/src/sync-protocols/near/fetcher.ts +274 -0
  32. package/src/sync-protocols/near/state.ts +130 -0
  33. package/src/sync-protocols/near/types.ts +46 -0
  34. package/src/sync-protocols/ntp/fetcher.ts +199 -0
  35. package/src/sync-protocols/ntp/state.ts +118 -0
  36. package/src/sync-protocols/ntp/types.ts +40 -0
  37. package/src/sync-protocols/orchestration/merge.ts +215 -0
  38. package/src/sync-protocols/orchestration/sync.ts +74 -0
  39. package/src/sync-protocols/types.ts +32 -0
  40. package/src/sync-protocols/utxorpc/BufferedRpc.ts +169 -0
  41. package/src/sync-protocols/utxorpc/fetcher.ts +116 -0
  42. package/src/sync-protocols/utxorpc/state.ts +186 -0
  43. package/src/sync-protocols/utxorpc/types.ts +53 -0
  44. package/src/sync-protocols/utxorpc/utils.ts +80 -0
  45. package/src/syncProtocolFactory.ts +149 -0
@@ -0,0 +1,116 @@
1
+ import { BaseDataFetcher, type DataFetched } from "../base/fetcher.ts";
2
+ import type { RootOutput, RootPage } from "../types.ts";
3
+ import type { Operation } from "effection";
4
+ import { bound, uint8ArrayToHexString } from "@effectstream/utils";
5
+ import type { ChainPoint, ConfigType, Input, Output, Page, PrimitiveType } from "./types.ts";
6
+ import type { OutputAndCleanup, RootConversion } from "../base/state.ts";
7
+ import type { BufferedRpc } from "./BufferedRpc.ts";
8
+ import { Buffer } from "node:buffer";
9
+ import type { cardano } from "@utxorpc/spec";
10
+ import { hashEqual, matchesPredicate } from "./utils.ts";
11
+
12
+ export class UtxoRpcFetcher
13
+ extends BaseDataFetcher<Input, Output, RootOutput, Page, RootPage> // PrimitiveFetcher<Input, Page, cardano.Block, PrimitiveType>,
14
+ {
15
+ constructor(
16
+ readonly config: ConfigType,
17
+ readonly client: BufferedRpc,
18
+ ) {
19
+ super(config.syncProtocol.name);
20
+ }
21
+
22
+ @bound
23
+ async startAsync(point?: ChainPoint) {
24
+ await this.client.start(point);
25
+ }
26
+
27
+ @bound
28
+ async resolveOrigin(): Promise<ChainPoint> {
29
+ return this.client.resolveOrigin();
30
+ }
31
+
32
+ @bound
33
+ override *readData(
34
+ data: Input,
35
+ rootConversion: RootConversion<Output, RootOutput, RootPage>,
36
+ ): Operation<DataFetched<Output, Page, RootPage>> {
37
+ console.log(
38
+ `[UTXORPC] Fetching blocks from ${data.from} to ${data.to}. ${
39
+ data.isPresync ? "[presync]" : ""
40
+ }`,
41
+ );
42
+ const outputs: OutputAndCleanup<Output>[] = [];
43
+ const blocks = this.client.fetchBlocks(data.from, data.to);
44
+ for (const block of blocks) {
45
+ outputs.push({
46
+ output: {
47
+ // TODO: What is the correct block hash?
48
+ blockHashes: [String(block.output.header?.hash)],
49
+ raw: block.output,
50
+ primitives: this.findPrimitives(block.output),
51
+ },
52
+ cleanup: block.cleanup,
53
+ });
54
+ }
55
+ return {
56
+ output: outputs,
57
+ lastPage: {
58
+ ownBlockNumber: Number(data.to),
59
+ own: {
60
+ slot: Number(
61
+ outputs[outputs.length - 1].output.raw.header!.slot,
62
+ ),
63
+ height: Number(
64
+ outputs[outputs.length - 1].output.raw.header!.height,
65
+ ),
66
+ hash: Buffer.from(
67
+ outputs[outputs.length - 1].output.raw.header!.hash,
68
+ )
69
+ .toString("hex"),
70
+ },
71
+ root: rootConversion.toRootPage({
72
+ blockHashes: [],
73
+ primitives: [], // TODO: I think this can be left empty here
74
+ raw: outputs[outputs.length - 1].output.raw,
75
+ }),
76
+ },
77
+ };
78
+ }
79
+
80
+ @bound
81
+ findPrimitives(block: cardano.Block): PrimitiveType[] {
82
+ const primitiveResponses: PrimitiveType[] = [];
83
+ for (const tx of block.body?.tx ?? []) {
84
+ let logIndex = 0;
85
+ for (const primitveEntry of this.config.primitives) {
86
+ if (!matchesPredicate(tx, primitveEntry.primitive.predicate)) {
87
+ continue;
88
+ }
89
+ const transactionIndex = block.body?.tx.findIndex(t => hashEqual(t.hash, tx.hash));
90
+ primitiveResponses.push({
91
+ syncProtocol: {
92
+ name: primitveEntry.syncProtocol,
93
+ blockNumber: Number(block.header!.height),
94
+ transactionHash: uint8ArrayToHexString(tx.hash),
95
+ transactionIndex,
96
+ contractAddress: '',
97
+ logIndex,
98
+ },
99
+ primitive: primitveEntry.primitive.name,
100
+ output: {
101
+ payloadType: 'utxorpc-response',
102
+ payload: { tx }
103
+ }
104
+ });
105
+ ++logIndex;
106
+ }
107
+ }
108
+ return primitiveResponses;
109
+ }
110
+
111
+ lastHeight(): bigint | undefined {
112
+ // Calling this.client.syncClient.getTip();
113
+ // returns the hash and slot, but not the block height
114
+ return this.client.lastHeight();
115
+ }
116
+ }
@@ -0,0 +1,186 @@
1
+ import { applyDelay } from "../common/utils.ts";
2
+ import type { Operation } from "effection";
3
+ import { call } from "effection";
4
+ import { getPage } from "@effectstream/db";
5
+ import type { PoolClient } from "pg";
6
+ import { bound, type TimestampMs } from "@effectstream/utils";
7
+ import { type LastPage, SyncState } from "../base/state.ts";
8
+ import type { RootOutput, RootPage } from "../types.ts";
9
+ import type { UtxoRpcFetcher } from "./fetcher.ts";
10
+ import type { ConfigType, Input, Output, Page } from "./types.ts";
11
+ import { chainPointRelation } from "./types.ts";
12
+ import { Buffer } from "node:buffer";
13
+
14
+ export class UtxoRpcSyncState extends SyncState<
15
+ Input,
16
+ Output,
17
+ Page,
18
+ RootOutput,
19
+ RootPage,
20
+ UtxoRpcFetcher
21
+ > {
22
+ constructor(
23
+ lastPage: undefined | LastPage<Page, RootPage>,
24
+ readonly config: ConfigType,
25
+ fetcher: UtxoRpcFetcher,
26
+ dbConn: PoolClient,
27
+ ) {
28
+ super(
29
+ config.syncProtocol.name,
30
+ lastPage,
31
+ fetcher,
32
+ chainPointRelation,
33
+ dbConn,
34
+ );
35
+ }
36
+
37
+ @bound
38
+ override *startAsync(): Operation<void> {
39
+ if (this.lastPage == null) {
40
+ const cp = this.config.syncProtocol.startChainPoint;
41
+ if (cp === "origin") {
42
+ const origin = yield* call(() => this.fetcher.resolveOrigin());
43
+ yield* call(() => this.fetcher.startAsync(origin));
44
+ } else if (cp === "tip") {
45
+ yield* call(() => this.fetcher.startAsync(undefined));
46
+ } else {
47
+ yield* call(() => this.fetcher.startAsync({ slot: cp.slot, hash: cp.hash }));
48
+ }
49
+ return;
50
+ }
51
+ yield* call(() =>
52
+ this.fetcher.startAsync({
53
+ slot: this.lastPage!.own.slot,
54
+ hash: this.lastPage!.own.hash,
55
+ })
56
+ );
57
+ }
58
+
59
+ @bound
60
+ override toRootOutput(data: Output): RootOutput {
61
+ throw new Error("Only main chains create root outputs");
62
+ }
63
+
64
+ @bound
65
+ override toRootPage(data: Output): RootPage {
66
+ return applyDelay(
67
+ Number(data.raw.timestamp * 1000n),
68
+ this.config.syncProtocol.delayMs,
69
+ );
70
+ }
71
+
72
+ @bound
73
+ override *stateToInput(): Operation<Input | undefined> {
74
+ // Get the value of the latest block height.
75
+ const tipHeight = yield* call(() => this.fetcher.lastHeight());
76
+ if (!tipHeight) {
77
+ return undefined;
78
+ }
79
+
80
+ // Using 0 as the startHeight for both "origin" and a specified ChainPoint is safe because:
81
+ // the FollowTip stream only buffers blocks starting from the intersection,
82
+ // so fetchBlocks(from=1) will match the first block, regardless of it's height.
83
+ // After that first block, lastPage.own.height will give a real height value.
84
+ const cp = this.config.syncProtocol.startChainPoint;
85
+ const startHeight = this.lastPage?.own.height ??
86
+ (cp === "tip"
87
+ ? Number(tipHeight) - 1
88
+ : 0);
89
+
90
+ if (BigInt(startHeight) >= tipHeight) {
91
+ return undefined;
92
+ }
93
+
94
+ // TODO We need to check how to handle the presync process.
95
+ // const storedBlocks = yield* this.fetcher.client.storedBlocks(
96
+ // this.lastPage?.own.height,
97
+ // );
98
+ // if (storedBlocks.from.slot < this.config.syncProtocol.startSlot) {
99
+ // return {
100
+ // from: storedBlocks.from.height,
101
+ // to: Math.max(
102
+ // storedBlocks.to.height,
103
+ // // TODO: this should be a height and not a slot number
104
+ // // either we need to resolve the actual block at that slot
105
+ // // (blocked by https://github.com/utxorpc/spec/issues/148)
106
+ // // or we need to guess and refine if wrong
107
+ // this.config.syncProtocol.startSlot - 1,
108
+ // ),
109
+ // isPresync: true,
110
+ // };
111
+ // }
112
+
113
+ // TODO Is this correct?
114
+ // EVM Handles this with `genInputRange`
115
+ const from = startHeight + 1;
116
+ const to = tipHeight ? Math.min(
117
+ from + 1,
118
+ Number(tipHeight),
119
+ ) : from + 1;
120
+
121
+ return {
122
+ from,
123
+ to,
124
+ isPresync: false, // TODO: handle presync
125
+ };
126
+ }
127
+
128
+ @bound
129
+ override toPage(
130
+ input: Input,
131
+ data: Output[],
132
+ ): Page {
133
+ return {
134
+ slot: Number(data[data.length - 1].raw.header!.slot),
135
+ height: Number(data[data.length - 1].raw.header!.height),
136
+ hash: Buffer.from(data[data.length - 1].raw.header!.hash).toString(
137
+ "hex",
138
+ ),
139
+ };
140
+ }
141
+
142
+ @bound
143
+ override mergeDatum(
144
+ ourOutput: Output,
145
+ rootOutput: RootOutput,
146
+ ): void {
147
+ const primitives = ourOutput.primitives.map((p) => ({
148
+ ...p,
149
+ source: this.config.syncProtocol.name,
150
+ }));
151
+ const blockInfo = ourOutput.blockHashes.map((h) => ({
152
+ protocol_name: this.config.syncProtocol.name,
153
+ block_number: Number(ourOutput.raw.header!.height),
154
+ blockHash: h,
155
+ }));
156
+ rootOutput.primitives.push(...primitives);
157
+ rootOutput.blockInfo.push(...blockInfo);
158
+ }
159
+
160
+ @bound
161
+ override getNamespace(): string[] {
162
+ return [this.config.network.name, this.config.syncProtocol.name];
163
+ }
164
+
165
+ static *restoreState(
166
+ dbConn: PoolClient,
167
+ config: ConfigType,
168
+ fetcher: UtxoRpcFetcher,
169
+ ): Operation<UtxoRpcSyncState> {
170
+ // TODO: move this DB query into page-helpers?
171
+ const [result] = yield* call(async () =>
172
+ await getPage.run({
173
+ protocol_name: config.syncProtocol.name,
174
+ }, dbConn)
175
+ );
176
+ const page = result
177
+ ? result.page as unknown as LastPage<Page, RootPage>
178
+ : undefined;
179
+ return new UtxoRpcSyncState(
180
+ page,
181
+ config,
182
+ fetcher,
183
+ dbConn,
184
+ );
185
+ }
186
+ }
@@ -0,0 +1,53 @@
1
+ import type {
2
+ AbsoluteSlotNumber,
3
+ BlockNumber,
4
+ CardanoBlockHash,
5
+ CardanoBlockNumber,
6
+ TimestampMs,
7
+ } from "@effectstream/utils";
8
+ import type { PageRelation } from "../base/page.ts";
9
+ import type { cardano } from "@utxorpc/spec";
10
+ import type { PageSyncRange } from "../common/page-helpers.ts";
11
+ import type {
12
+ ConfigSyncProtocolType,
13
+ FlattenSyncProtocolIOFor,
14
+ PrimitiveEntry,
15
+ SyncProtocolWithNetwork,
16
+ } from "@effectstream/config";
17
+
18
+ import type { CardanoChainPoint } from "@utxorpc/sdk";
19
+ export type ChainPoint = CardanoChainPoint;
20
+
21
+ export type Page = {
22
+ slot: AbsoluteSlotNumber;
23
+ height: CardanoBlockNumber;
24
+ hash: CardanoBlockHash;
25
+ };
26
+
27
+ export type PrimitiveEntryType = Extract<
28
+ PrimitiveEntry,
29
+ { syncProtocol: ConfigSyncProtocolType.CARDANO_UTXORPC_PARALLEL }
30
+ >;
31
+
32
+ // TODO: blocked on https://github.com/utxorpc/spec/issues/135
33
+ export type PrimitiveType = FlattenSyncProtocolIOFor<
34
+ ConfigSyncProtocolType.CARDANO_UTXORPC_PARALLEL
35
+ >;
36
+ export type Input = PageSyncRange<BlockNumber>;
37
+ export type Output = {
38
+ raw: cardano.Block;
39
+ primitives: PrimitiveType[];
40
+ blockHashes: CardanoBlockHash[];
41
+ };
42
+
43
+ export const chainPointRelation: PageRelation<Page> = {
44
+ compare: (p1, p2) => p1.slot - p2.slot,
45
+ equals: (p1, p2) => p1.slot === p2.slot,
46
+ min: (p1, p2) => (p1.slot < p2.slot ? p1 : p2),
47
+ max: (p1, p2) => (p1.slot > p2.slot ? p1 : p2),
48
+ };
49
+
50
+ export type ConfigType = Extract<
51
+ SyncProtocolWithNetwork,
52
+ { syncProtocolType: ConfigSyncProtocolType.CARDANO_UTXORPC_PARALLEL }
53
+ >;
@@ -0,0 +1,80 @@
1
+ import type { cardano } from '@utxorpc/spec';
2
+ import type { UtxorpcAddressPattern, UtxorpcAnyChainTxPattern, UtxorpcAssetPattern, UtxorpcTxOutputPattern, UtxorpcTxPredicate } from "@effectstream/config";
3
+ import { hexStringToUint8Array } from "@effectstream/utils";
4
+ import { Cardano } from '@cardano-sdk/core';
5
+ import { HexBlob } from '@cardano-sdk/util';
6
+
7
+ export function hashEqual(lhs: Uint8Array, rhs: Uint8Array): boolean {
8
+ if (lhs.length !== rhs.length) {
9
+ return false;
10
+ }
11
+ for (let i = 0; i < lhs.length; ++i) {
12
+ if (lhs[i] !== rhs[i]) {
13
+ return false;
14
+ }
15
+ }
16
+ return true;
17
+ }
18
+
19
+ export function matchesPredicate(tx: cardano.Tx, predicate: UtxorpcTxPredicate): boolean {
20
+ if (predicate.match && !matchesPattern(tx, predicate.match)) {
21
+ return false;
22
+ }
23
+ if (predicate.not && predicate.not.some(p => matchesPredicate(tx, p))) {
24
+ return false;
25
+ }
26
+ if (predicate.all_of && !predicate.all_of.every(p => matchesPredicate(tx, p))) {
27
+ return false;
28
+ }
29
+ if (predicate.any_of && !predicate.any_of.some(p => matchesPredicate(tx, p))) {
30
+ return false;
31
+ }
32
+ return true;
33
+ }
34
+
35
+ function matchesPattern(tx: cardano.Tx, pattern: UtxorpcAnyChainTxPattern): boolean {
36
+ const cardanoPattern = pattern.cardano;
37
+ if (!cardanoPattern) return false;
38
+ const outputs = tx.outputs;
39
+ const inputs = tx.inputs.map(input => input.asOutput!).filter(x => x);
40
+ const matchHasAddress = !cardanoPattern.has_address || matchesAddress(outputs, cardanoPattern.has_address) || matchesAddress(inputs, cardanoPattern.has_address);
41
+ const matchMovesAsset = !cardanoPattern.moves_asset || matchesAsset(inputs, cardanoPattern.moves_asset);
42
+ const matchMintsAsset = !cardanoPattern.mints_asset || matchesAsset(outputs, cardanoPattern.mints_asset);
43
+ return matchHasAddress && matchMovesAsset && matchMintsAsset;
44
+ }
45
+
46
+ function matchesOutputPattern(outputs: cardano.TxOutput[], pattern: UtxorpcTxOutputPattern): boolean {
47
+ const matchAddress = !pattern.address || matchesAddress(outputs, pattern.address);
48
+ const matchAsset = !pattern.asset || matchesAsset(outputs, pattern.asset);
49
+ return matchAddress && matchAsset;
50
+ }
51
+
52
+ function matchesAddress(outputs: cardano.TxOutput[], pattern: UtxorpcAddressPattern): boolean {
53
+ if (pattern.exact_address) {
54
+ const address = Uint8Array.from(atob(pattern.exact_address), c => c.charCodeAt(0));
55
+ if (!outputs.some(o => hashEqual(o.address, address))) {
56
+ return false;
57
+ }
58
+ }
59
+ if (pattern.payment_part || pattern.delegation_part) {
60
+ const addresses = outputs.map(o => Cardano.Address.fromBytes(HexBlob.fromBytes(o.address)));
61
+ if (pattern.payment_part && !addresses.some(a => a.getProps()?.paymentPart?.hash === pattern.payment_part)) {
62
+ return false;
63
+ }
64
+ if (pattern.delegation_part && !addresses.some(a => a.getProps()?.delegationPart?.hash === pattern.delegation_part)) {
65
+ return false;
66
+ }
67
+ }
68
+ return true;
69
+ }
70
+
71
+ function matchesAsset(outputs: cardano.TxOutput[], pattern: UtxorpcAssetPattern): boolean {
72
+ const policyId = pattern.policy_id ? hexStringToUint8Array(pattern.policy_id) : null;
73
+ const assetName = pattern.asset_name ? hexStringToUint8Array(pattern.asset_name) : null;
74
+ return outputs.some(o => o.assets.some(ma => {
75
+ if (policyId && !hashEqual(policyId, ma.policyId)) {
76
+ return false;
77
+ }
78
+ return ma.assets.some(a => !assetName || hashEqual(a.name, assetName));
79
+ }));
80
+ }
@@ -0,0 +1,149 @@
1
+ import { EvmSyncState } from "./sync-protocols/evm/state.ts";
2
+ import { EvmFetcher } from "./sync-protocols/evm/fetcher.ts";
3
+ import type { Operation } from "effection";
4
+ import type { PoolClient } from "pg";
5
+ import type { AllSyncProtocols } from "./sync-protocols/types.ts";
6
+ import { createViemPublicClient } from "@effectstream/utils";
7
+ import type { SyncProtocolWithNetwork } from "@effectstream/config";
8
+ import {
9
+ ConfigNetworkType,
10
+ ConfigSyncProtocolType,
11
+ getViemNetwork,
12
+ } from "@effectstream/config";
13
+ import { CardanoSyncClient } from "@utxorpc/sdk";
14
+ import { BufferedRpc } from "./sync-protocols/utxorpc/BufferedRpc.ts";
15
+ import { UtxoRpcFetcher } from "./sync-protocols/utxorpc/fetcher.ts";
16
+ import { UtxoRpcSyncState } from "./sync-protocols/utxorpc/state.ts";
17
+ import { MidnightFetcher, MidnightSyncState } from "@effectstream/sync";
18
+ import { AvailFetcher } from "./sync-protocols/avail/fetcher.ts";
19
+ import { AvailSyncState } from "./sync-protocols/avail/state.ts";
20
+ import { NtpFetcher } from "./sync-protocols/ntp/fetcher.ts";
21
+ import { NtpSyncState } from "./sync-protocols/ntp/state.ts";
22
+ import {
23
+ BitcoinFetcher,
24
+ BitcoinRpcClient,
25
+ } from "./sync-protocols/bitcoin/fetcher.ts";
26
+ import { BitcoinSyncState } from "./sync-protocols/bitcoin/state.ts";
27
+ import { CelestiaFetcher } from "./sync-protocols/celestia/fetcher.ts";
28
+ import { CelestiaSyncState } from "./sync-protocols/celestia/state.ts";
29
+ import { NearFetcher } from "./sync-protocols/near/fetcher.ts";
30
+ import { NearSyncState } from "./sync-protocols/near/state.ts";
31
+
32
+ export function* genSyncProtocols(
33
+ dbConn: PoolClient,
34
+ syncInfo: SyncProtocolWithNetwork[],
35
+ ): Operation<AllSyncProtocols[]> {
36
+ const result: AllSyncProtocols[] = [];
37
+ for (const entry of syncInfo) {
38
+ if (entry.networkType === ConfigNetworkType.NTP) {
39
+ const fetcher = new NtpFetcher(entry);
40
+ const state = yield* NtpSyncState.restoreState(
41
+ dbConn,
42
+ entry,
43
+ fetcher,
44
+ );
45
+ result.push(state);
46
+ } else if (
47
+ entry.networkType === ConfigNetworkType.EVM
48
+ ) {
49
+ const viemNetwork = yield* getViemNetwork(entry.network);
50
+ const fetcher = new EvmFetcher(
51
+ entry,
52
+ createViemPublicClient(viemNetwork, {
53
+ cacheTime: entry.syncProtocol.pollingInterval,
54
+ }),
55
+ );
56
+ const state = yield* EvmSyncState.restoreState(
57
+ dbConn,
58
+ entry,
59
+ fetcher,
60
+ );
61
+ result.push(state);
62
+ } else if (
63
+ entry.networkType === ConfigNetworkType.CARDANO
64
+ ) {
65
+ if (
66
+ entry.syncProtocolType === ConfigSyncProtocolType.CARDANO_CARP_PARALLEL
67
+ ) {
68
+ throw new Error("CARP not supported yet");
69
+ }
70
+ const syncClient = new CardanoSyncClient({
71
+ uri: entry.syncProtocol.rpcUrl,
72
+ headers: entry.syncProtocol.headers,
73
+ });
74
+ const bufferedRpc = new BufferedRpc(
75
+ syncClient,
76
+ entry.syncProtocol.confirmationDepth,
77
+ );
78
+ const fetcher = new UtxoRpcFetcher(
79
+ entry,
80
+ bufferedRpc,
81
+ );
82
+ const state = yield* UtxoRpcSyncState.restoreState(
83
+ dbConn,
84
+ entry,
85
+ fetcher,
86
+ );
87
+ result.push(state);
88
+ } else if (
89
+ entry.networkType === ConfigNetworkType.MIDNIGHT
90
+ ) {
91
+ const fetcher = new MidnightFetcher(entry);
92
+ const state = yield* MidnightSyncState.restoreState(
93
+ dbConn,
94
+ entry,
95
+ fetcher,
96
+ );
97
+ result.push(state);
98
+ } else if (
99
+ entry.networkType === ConfigNetworkType.AVAIL
100
+ ) {
101
+ const fetcher = new AvailFetcher(entry);
102
+ const state = yield* AvailSyncState.restoreState(
103
+ dbConn,
104
+ entry,
105
+ fetcher,
106
+ );
107
+ result.push(state);
108
+ } else if (
109
+ entry.networkType === ConfigNetworkType.BITCOIN
110
+ ) {
111
+ const rpcClient = new BitcoinRpcClient({
112
+ url: entry.network.rpcUrl,
113
+ username: entry.network.rpcAuth?.username ?? null,
114
+ password: entry.network.rpcAuth?.password ?? null,
115
+ });
116
+ const fetcher = new BitcoinFetcher(entry, rpcClient);
117
+ const state = yield* BitcoinSyncState.restoreState(
118
+ dbConn,
119
+ entry,
120
+ fetcher,
121
+ );
122
+ result.push(state);
123
+ } else if (
124
+ entry.networkType === ConfigNetworkType.CELESTIA
125
+ ) {
126
+ const fetcher = new CelestiaFetcher(entry);
127
+ const state = yield* CelestiaSyncState.restoreState(
128
+ dbConn,
129
+ entry,
130
+ fetcher,
131
+ );
132
+ result.push(state);
133
+ } else if (
134
+ entry.networkType === ConfigNetworkType.NEAR
135
+ ) {
136
+ const fetcher = new NearFetcher(entry);
137
+ const state = yield* NearSyncState.restoreState(
138
+ dbConn,
139
+ entry,
140
+ fetcher,
141
+ );
142
+ result.push(state);
143
+ } else {
144
+ throw new Error(`Unsupported network type: ${entry.network.type}`);
145
+ }
146
+ }
147
+
148
+ return result;
149
+ }