@develit-services/blockchain 0.6.2 → 0.7.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.
Files changed (48) hide show
  1. package/dist/database/schema.cjs +2 -1
  2. package/dist/database/schema.d.cts +1 -1
  3. package/dist/database/schema.d.mts +1 -1
  4. package/dist/database/schema.d.ts +1 -1
  5. package/dist/database/schema.mjs +1 -1
  6. package/dist/export/worker.cjs +100 -7
  7. package/dist/export/worker.d.cts +4 -5
  8. package/dist/export/worker.d.mts +4 -5
  9. package/dist/export/worker.d.ts +4 -5
  10. package/dist/export/worker.mjs +100 -7
  11. package/dist/export/workflows.cjs +124 -45
  12. package/dist/export/workflows.mjs +125 -46
  13. package/dist/export/wrangler.cjs +8 -4
  14. package/dist/export/wrangler.d.cts +7 -4
  15. package/dist/export/wrangler.d.mts +7 -4
  16. package/dist/export/wrangler.d.ts +7 -4
  17. package/dist/export/wrangler.mjs +8 -4
  18. package/dist/shared/blockchain.0tUJ62WT.mjs +6 -0
  19. package/dist/shared/blockchain.4Hzh__vM.d.ts +61 -0
  20. package/dist/shared/blockchain.B49xpPZ0.cjs +36 -0
  21. package/dist/shared/blockchain.BBvwu2_7.cjs +39 -0
  22. package/dist/shared/blockchain.BDDFE27V.d.mts +61 -0
  23. package/dist/shared/blockchain.B_Tqqlia.mjs +31 -0
  24. package/dist/shared/{blockchain.BOT9dDRh.d.cts → blockchain.C1Jdisxn.d.cts} +9 -4
  25. package/dist/shared/{blockchain.BOT9dDRh.d.mts → blockchain.C1Jdisxn.d.mts} +9 -4
  26. package/dist/shared/{blockchain.BOT9dDRh.d.ts → blockchain.C1Jdisxn.d.ts} +9 -4
  27. package/dist/shared/blockchain.Cx60lJ0c.d.cts +566 -0
  28. package/dist/shared/blockchain.Cx60lJ0c.d.mts +566 -0
  29. package/dist/shared/blockchain.Cx60lJ0c.d.ts +566 -0
  30. package/dist/shared/blockchain.DN735AwB.cjs +8 -0
  31. package/dist/shared/blockchain.DbsOmkkX.d.cts +61 -0
  32. package/dist/shared/blockchain._wwKu1qP.mjs +35 -0
  33. package/dist/types.cjs +4 -1
  34. package/dist/types.d.cts +35 -2
  35. package/dist/types.d.mts +35 -2
  36. package/dist/types.d.ts +35 -2
  37. package/dist/types.mjs +2 -1
  38. package/package.json +1 -1
  39. package/dist/shared/blockchain.55Jm5hWs.mjs +0 -11
  40. package/dist/shared/blockchain.D63utaAU.cjs +0 -14
  41. package/dist/shared/blockchain.DmhmTTL_.mjs +0 -17
  42. package/dist/shared/blockchain.JxC5JMLI.cjs +0 -20
  43. package/dist/shared/blockchain.QCUmm8Hp.d.cts +0 -12
  44. package/dist/shared/blockchain.QCUmm8Hp.d.mts +0 -12
  45. package/dist/shared/blockchain.QCUmm8Hp.d.ts +0 -12
  46. package/dist/shared/blockchain.f4eN2PFQ.d.cts +0 -97
  47. package/dist/shared/blockchain.f4eN2PFQ.d.mts +0 -97
  48. package/dist/shared/blockchain.f4eN2PFQ.d.ts +0 -97
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const database_schema = require('../shared/blockchain.JxC5JMLI.cjs');
3
+ const database_schema = require('../shared/blockchain.BBvwu2_7.cjs');
4
4
  require('@develit-io/backend-sdk');
5
5
  require('drizzle-orm/sqlite-core');
6
6
 
7
7
 
8
8
 
9
9
  exports.address = database_schema.address;
10
+ exports.transaction = database_schema.transaction;
@@ -1,2 +1,2 @@
1
- export { a as address } from '../shared/blockchain.f4eN2PFQ.cjs';
1
+ export { a as address, t as transaction } from '../shared/blockchain.Cx60lJ0c.cjs';
2
2
  import 'drizzle-orm/sqlite-core';
@@ -1,2 +1,2 @@
1
- export { a as address } from '../shared/blockchain.f4eN2PFQ.mjs';
1
+ export { a as address, t as transaction } from '../shared/blockchain.Cx60lJ0c.mjs';
2
2
  import 'drizzle-orm/sqlite-core';
@@ -1,2 +1,2 @@
1
- export { a as address } from '../shared/blockchain.f4eN2PFQ.js';
1
+ export { a as address, t as transaction } from '../shared/blockchain.Cx60lJ0c.js';
2
2
  import 'drizzle-orm/sqlite-core';
@@ -1,3 +1,3 @@
1
- export { a as address } from '../shared/blockchain.DmhmTTL_.mjs';
1
+ export { a as address, t as transaction } from '../shared/blockchain._wwKu1qP.mjs';
2
2
  import '@develit-io/backend-sdk';
3
3
  import 'drizzle-orm/sqlite-core';
@@ -3,15 +3,89 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const backendSdk = require('@develit-io/backend-sdk');
6
- const database_schema = require('../shared/blockchain.JxC5JMLI.cjs');
7
- require('drizzle-orm');
8
- const syncAddress = require('../shared/blockchain.D63utaAU.cjs');
6
+ const viem = require('viem');
7
+ const drizzle = require('../shared/blockchain.DN735AwB.cjs');
8
+ const syncAddress = require('../shared/blockchain.B49xpPZ0.cjs');
9
9
  const cloudflare_workers = require('cloudflare:workers');
10
10
  const d1 = require('drizzle-orm/d1');
11
+ require('../shared/blockchain.BBvwu2_7.cjs');
11
12
  require('drizzle-orm/sqlite-core');
13
+ require('drizzle-orm');
12
14
  require('zod');
13
15
 
14
- const tables = database_schema.schema;
16
+ class IBlockchainConnector {
17
+ }
18
+
19
+ class AlchemyConnector extends IBlockchainConnector {
20
+ constructor(config) {
21
+ super();
22
+ this.connectorKey = "ALCHEMY";
23
+ this.client = null;
24
+ this.rpcUrl = config.rpcUrl;
25
+ }
26
+ createClient() {
27
+ if (!this.client) {
28
+ this.client = viem.createPublicClient({
29
+ transport: viem.http(this.rpcUrl)
30
+ });
31
+ }
32
+ return this.client;
33
+ }
34
+ async getTransaction(txHash) {
35
+ const client = this.createClient();
36
+ const [tx, txError] = await backendSdk.useResult(
37
+ client.getTransaction({ hash: txHash })
38
+ );
39
+ if (txError || !tx) {
40
+ throw backendSdk.createInternalError(txError);
41
+ }
42
+ let status = "pending";
43
+ let gasUsed = null;
44
+ let timestamp = null;
45
+ if (tx.blockNumber) {
46
+ const [receipt, receiptError] = await backendSdk.useResult(
47
+ client.getTransactionReceipt({ hash: txHash })
48
+ );
49
+ if (receiptError || !receipt) {
50
+ throw backendSdk.createInternalError(receiptError);
51
+ }
52
+ const [block, blockError] = await backendSdk.useResult(
53
+ client.getBlock({ blockNumber: tx.blockNumber })
54
+ );
55
+ if (blockError || !block) {
56
+ throw backendSdk.createInternalError(blockError);
57
+ }
58
+ status = receipt.status === "success" ? "success" : "reverted";
59
+ gasUsed = receipt.gasUsed.toString();
60
+ timestamp = Number(block.timestamp) * 1e3;
61
+ }
62
+ return {
63
+ hash: tx.hash,
64
+ from: tx.from,
65
+ to: tx.to,
66
+ value: tx.value.toString(),
67
+ blockNumber: tx.blockNumber?.toString() ?? null,
68
+ blockHash: tx.blockHash ?? null,
69
+ gasPrice: tx.gasPrice?.toString() ?? null,
70
+ gas: tx.gas.toString(),
71
+ nonce: tx.nonce,
72
+ transactionIndex: tx.transactionIndex,
73
+ status,
74
+ gasUsed,
75
+ timestamp
76
+ };
77
+ }
78
+ async getTransactionReceipt(txHash) {
79
+ const client = this.createClient();
80
+ const [receipt, error] = await backendSdk.useResult(
81
+ client.getTransactionReceipt({ hash: txHash })
82
+ );
83
+ if (error || !receipt) {
84
+ throw backendSdk.createInternalError(error);
85
+ }
86
+ return receipt;
87
+ }
88
+ }
15
89
 
16
90
  var __defProp = Object.defineProperty;
17
91
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -28,20 +102,36 @@ let BlockchainServiceBase = class extends backendSdk.develitWorker(
28
102
  ) {
29
103
  constructor(ctx, env) {
30
104
  super(ctx, env);
31
- this.db = d1.drizzle(this.env.BLOCKCHAIN_D1, { schema: tables });
105
+ this.db = d1.drizzle(this.env.BLOCKCHAIN_D1, { schema: drizzle.tables });
32
106
  }
33
107
  async syncAddress(input) {
34
108
  return this.handleAction(
35
109
  { data: input, schema: syncAddress.syncAddressInputSchema },
36
110
  { successMessage: "Address sync workflow started" },
37
111
  async ({ addressId }) => {
38
- await this.env.SYNC_ADDRESS_TRANSACTIONS_WORKFLOW.create({
112
+ const instance = await this.env.SYNC_ADDRESS_TRANSACTIONS_WORKFLOW.create({
39
113
  id: addressId,
40
114
  params: {
41
115
  addressId
42
116
  }
43
117
  });
44
- return {};
118
+ return {
119
+ instanceId: instance.id,
120
+ details: await instance.status()
121
+ };
122
+ }
123
+ );
124
+ }
125
+ async getTransaction(input) {
126
+ return this.handleAction(
127
+ { data: input, schema: syncAddress.getTransactionInputSchema },
128
+ { successMessage: "Transaction retrieved successfully." },
129
+ async ({ txHash }) => {
130
+ const rpcUrl = (await this.env.SECRETS_STORE.get({
131
+ secretName: "BLOCKCHAIN_SERVICE_RPC_URL"
132
+ })).data?.secretValue || "";
133
+ const connector = new AlchemyConnector({ rpcUrl });
134
+ return connector.getTransaction(txHash);
45
135
  }
46
136
  );
47
137
  }
@@ -49,6 +139,9 @@ let BlockchainServiceBase = class extends backendSdk.develitWorker(
49
139
  __decorateClass([
50
140
  backendSdk.action("sync-address")
51
141
  ], BlockchainServiceBase.prototype, "syncAddress", 1);
142
+ __decorateClass([
143
+ backendSdk.action("get-transaction")
144
+ ], BlockchainServiceBase.prototype, "getTransaction", 1);
52
145
  BlockchainServiceBase = __decorateClass([
53
146
  backendSdk.service("blockchain")
54
147
  ], BlockchainServiceBase);
@@ -1,19 +1,18 @@
1
1
  import * as _develit_io_backend_sdk from '@develit-io/backend-sdk';
2
2
  import { IRPCResponse } from '@develit-io/backend-sdk';
3
- import { s as schema } from '../shared/blockchain.f4eN2PFQ.cjs';
4
- import { S as SyncAddressInput, a as SyncAddressOutput } from '../shared/blockchain.QCUmm8Hp.cjs';
3
+ import { t as tables, S as SyncAddressInput, a as SyncAddressOutput, G as GetTransactionInput, b as GetTransactionOutput } from '../shared/blockchain.DbsOmkkX.cjs';
5
4
  import { WorkerEntrypoint } from 'cloudflare:workers';
6
5
  import { DrizzleD1Database } from 'drizzle-orm/d1';
7
- import 'drizzle-orm/sqlite-core';
8
6
  import 'zod';
9
-
10
- declare const tables: typeof schema;
7
+ import '../shared/blockchain.Cx60lJ0c.cjs';
8
+ import 'drizzle-orm/sqlite-core';
11
9
 
12
10
  declare const BlockchainServiceBase_base: (abstract new (ctx: ExecutionContext, env: BlockchainEnv) => WorkerEntrypoint<BlockchainEnv, {}>) & (abstract new (...args: any[]) => _develit_io_backend_sdk.DevelitWorkerMethods);
13
11
  declare class BlockchainServiceBase extends BlockchainServiceBase_base {
14
12
  readonly db: DrizzleD1Database<typeof tables>;
15
13
  constructor(ctx: ExecutionContext, env: BlockchainEnv);
16
14
  syncAddress(input: SyncAddressInput): Promise<IRPCResponse<SyncAddressOutput>>;
15
+ getTransaction(input: GetTransactionInput): Promise<IRPCResponse<GetTransactionOutput>>;
17
16
  }
18
17
  declare function defineBlockchainService(): new (ctx: ExecutionContext, env: BlockchainEnv) => BlockchainServiceBase;
19
18
 
@@ -1,19 +1,18 @@
1
1
  import * as _develit_io_backend_sdk from '@develit-io/backend-sdk';
2
2
  import { IRPCResponse } from '@develit-io/backend-sdk';
3
- import { s as schema } from '../shared/blockchain.f4eN2PFQ.mjs';
4
- import { S as SyncAddressInput, a as SyncAddressOutput } from '../shared/blockchain.QCUmm8Hp.mjs';
3
+ import { t as tables, S as SyncAddressInput, a as SyncAddressOutput, G as GetTransactionInput, b as GetTransactionOutput } from '../shared/blockchain.BDDFE27V.mjs';
5
4
  import { WorkerEntrypoint } from 'cloudflare:workers';
6
5
  import { DrizzleD1Database } from 'drizzle-orm/d1';
7
- import 'drizzle-orm/sqlite-core';
8
6
  import 'zod';
9
-
10
- declare const tables: typeof schema;
7
+ import '../shared/blockchain.Cx60lJ0c.mjs';
8
+ import 'drizzle-orm/sqlite-core';
11
9
 
12
10
  declare const BlockchainServiceBase_base: (abstract new (ctx: ExecutionContext, env: BlockchainEnv) => WorkerEntrypoint<BlockchainEnv, {}>) & (abstract new (...args: any[]) => _develit_io_backend_sdk.DevelitWorkerMethods);
13
11
  declare class BlockchainServiceBase extends BlockchainServiceBase_base {
14
12
  readonly db: DrizzleD1Database<typeof tables>;
15
13
  constructor(ctx: ExecutionContext, env: BlockchainEnv);
16
14
  syncAddress(input: SyncAddressInput): Promise<IRPCResponse<SyncAddressOutput>>;
15
+ getTransaction(input: GetTransactionInput): Promise<IRPCResponse<GetTransactionOutput>>;
17
16
  }
18
17
  declare function defineBlockchainService(): new (ctx: ExecutionContext, env: BlockchainEnv) => BlockchainServiceBase;
19
18
 
@@ -1,19 +1,18 @@
1
1
  import * as _develit_io_backend_sdk from '@develit-io/backend-sdk';
2
2
  import { IRPCResponse } from '@develit-io/backend-sdk';
3
- import { s as schema } from '../shared/blockchain.f4eN2PFQ.js';
4
- import { S as SyncAddressInput, a as SyncAddressOutput } from '../shared/blockchain.QCUmm8Hp.js';
3
+ import { t as tables, S as SyncAddressInput, a as SyncAddressOutput, G as GetTransactionInput, b as GetTransactionOutput } from '../shared/blockchain.4Hzh__vM.js';
5
4
  import { WorkerEntrypoint } from 'cloudflare:workers';
6
5
  import { DrizzleD1Database } from 'drizzle-orm/d1';
7
- import 'drizzle-orm/sqlite-core';
8
6
  import 'zod';
9
-
10
- declare const tables: typeof schema;
7
+ import '../shared/blockchain.Cx60lJ0c.js';
8
+ import 'drizzle-orm/sqlite-core';
11
9
 
12
10
  declare const BlockchainServiceBase_base: (abstract new (ctx: ExecutionContext, env: BlockchainEnv) => WorkerEntrypoint<BlockchainEnv, {}>) & (abstract new (...args: any[]) => _develit_io_backend_sdk.DevelitWorkerMethods);
13
11
  declare class BlockchainServiceBase extends BlockchainServiceBase_base {
14
12
  readonly db: DrizzleD1Database<typeof tables>;
15
13
  constructor(ctx: ExecutionContext, env: BlockchainEnv);
16
14
  syncAddress(input: SyncAddressInput): Promise<IRPCResponse<SyncAddressOutput>>;
15
+ getTransaction(input: GetTransactionInput): Promise<IRPCResponse<GetTransactionOutput>>;
17
16
  }
18
17
  declare function defineBlockchainService(): new (ctx: ExecutionContext, env: BlockchainEnv) => BlockchainServiceBase;
19
18
 
@@ -1,13 +1,87 @@
1
- import { develitWorker, action, service } from '@develit-io/backend-sdk';
2
- import { s as schema } from '../shared/blockchain.DmhmTTL_.mjs';
3
- import 'drizzle-orm';
4
- import { s as syncAddressInputSchema } from '../shared/blockchain.55Jm5hWs.mjs';
1
+ import { useResult, createInternalError, develitWorker, action, service } from '@develit-io/backend-sdk';
2
+ import { createPublicClient, http } from 'viem';
3
+ import { t as tables } from '../shared/blockchain.0tUJ62WT.mjs';
4
+ import { s as syncAddressInputSchema, g as getTransactionInputSchema } from '../shared/blockchain.B_Tqqlia.mjs';
5
5
  import { WorkerEntrypoint } from 'cloudflare:workers';
6
6
  import { drizzle } from 'drizzle-orm/d1';
7
+ import '../shared/blockchain._wwKu1qP.mjs';
7
8
  import 'drizzle-orm/sqlite-core';
9
+ import 'drizzle-orm';
8
10
  import 'zod';
9
11
 
10
- const tables = schema;
12
+ class IBlockchainConnector {
13
+ }
14
+
15
+ class AlchemyConnector extends IBlockchainConnector {
16
+ constructor(config) {
17
+ super();
18
+ this.connectorKey = "ALCHEMY";
19
+ this.client = null;
20
+ this.rpcUrl = config.rpcUrl;
21
+ }
22
+ createClient() {
23
+ if (!this.client) {
24
+ this.client = createPublicClient({
25
+ transport: http(this.rpcUrl)
26
+ });
27
+ }
28
+ return this.client;
29
+ }
30
+ async getTransaction(txHash) {
31
+ const client = this.createClient();
32
+ const [tx, txError] = await useResult(
33
+ client.getTransaction({ hash: txHash })
34
+ );
35
+ if (txError || !tx) {
36
+ throw createInternalError(txError);
37
+ }
38
+ let status = "pending";
39
+ let gasUsed = null;
40
+ let timestamp = null;
41
+ if (tx.blockNumber) {
42
+ const [receipt, receiptError] = await useResult(
43
+ client.getTransactionReceipt({ hash: txHash })
44
+ );
45
+ if (receiptError || !receipt) {
46
+ throw createInternalError(receiptError);
47
+ }
48
+ const [block, blockError] = await useResult(
49
+ client.getBlock({ blockNumber: tx.blockNumber })
50
+ );
51
+ if (blockError || !block) {
52
+ throw createInternalError(blockError);
53
+ }
54
+ status = receipt.status === "success" ? "success" : "reverted";
55
+ gasUsed = receipt.gasUsed.toString();
56
+ timestamp = Number(block.timestamp) * 1e3;
57
+ }
58
+ return {
59
+ hash: tx.hash,
60
+ from: tx.from,
61
+ to: tx.to,
62
+ value: tx.value.toString(),
63
+ blockNumber: tx.blockNumber?.toString() ?? null,
64
+ blockHash: tx.blockHash ?? null,
65
+ gasPrice: tx.gasPrice?.toString() ?? null,
66
+ gas: tx.gas.toString(),
67
+ nonce: tx.nonce,
68
+ transactionIndex: tx.transactionIndex,
69
+ status,
70
+ gasUsed,
71
+ timestamp
72
+ };
73
+ }
74
+ async getTransactionReceipt(txHash) {
75
+ const client = this.createClient();
76
+ const [receipt, error] = await useResult(
77
+ client.getTransactionReceipt({ hash: txHash })
78
+ );
79
+ if (error || !receipt) {
80
+ throw createInternalError(error);
81
+ }
82
+ return receipt;
83
+ }
84
+ }
11
85
 
12
86
  var __defProp = Object.defineProperty;
13
87
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -31,13 +105,29 @@ let BlockchainServiceBase = class extends develitWorker(
31
105
  { data: input, schema: syncAddressInputSchema },
32
106
  { successMessage: "Address sync workflow started" },
33
107
  async ({ addressId }) => {
34
- await this.env.SYNC_ADDRESS_TRANSACTIONS_WORKFLOW.create({
108
+ const instance = await this.env.SYNC_ADDRESS_TRANSACTIONS_WORKFLOW.create({
35
109
  id: addressId,
36
110
  params: {
37
111
  addressId
38
112
  }
39
113
  });
40
- return {};
114
+ return {
115
+ instanceId: instance.id,
116
+ details: await instance.status()
117
+ };
118
+ }
119
+ );
120
+ }
121
+ async getTransaction(input) {
122
+ return this.handleAction(
123
+ { data: input, schema: getTransactionInputSchema },
124
+ { successMessage: "Transaction retrieved successfully." },
125
+ async ({ txHash }) => {
126
+ const rpcUrl = (await this.env.SECRETS_STORE.get({
127
+ secretName: "BLOCKCHAIN_SERVICE_RPC_URL"
128
+ })).data?.secretValue || "";
129
+ const connector = new AlchemyConnector({ rpcUrl });
130
+ return connector.getTransaction(txHash);
41
131
  }
42
132
  );
43
133
  }
@@ -45,6 +135,9 @@ let BlockchainServiceBase = class extends develitWorker(
45
135
  __decorateClass([
46
136
  action("sync-address")
47
137
  ], BlockchainServiceBase.prototype, "syncAddress", 1);
138
+ __decorateClass([
139
+ action("get-transaction")
140
+ ], BlockchainServiceBase.prototype, "getTransaction", 1);
48
141
  BlockchainServiceBase = __decorateClass([
49
142
  service("blockchain")
50
143
  ], BlockchainServiceBase);
@@ -1,42 +1,61 @@
1
1
  'use strict';
2
2
 
3
+ const backendSdk = require('@develit-io/backend-sdk');
4
+ const drizzle = require('../shared/blockchain.DN735AwB.cjs');
5
+ const drizzleOrm = require('drizzle-orm');
3
6
  const cloudflare_workers = require('cloudflare:workers');
4
7
  const cloudflare_workflows = require('cloudflare:workflows');
8
+ const d1 = require('drizzle-orm/d1');
5
9
  const viem = require('viem');
6
10
  const chains = require('viem/chains');
11
+ require('../shared/blockchain.BBvwu2_7.cjs');
12
+ require('drizzle-orm/sqlite-core');
7
13
 
8
- const TEST_ADDRESSES = {
9
- "faucet-1": {
10
- id: "faucet-1",
11
- address: "0xc959483dba39aa9e78757139af0e9a2edeb3f42d",
12
- blockchain: "ethereum",
13
- network: "sepolia",
14
- syncIntervalS: 60,
15
- lastSyncedBlock: null
16
- },
17
- "faucet-2": {
18
- id: "faucet-2",
19
- address: "0x1f885520b7bd528e46b390040f12e753dce43004",
20
- blockchain: "ethereum",
21
- network: "sepolia",
22
- syncIntervalS: 90,
23
- lastSyncedBlock: null
24
- },
25
- "vitalik-test": {
26
- id: "vitalik-test",
27
- address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
28
- blockchain: "ethereum",
29
- network: "sepolia",
30
- syncIntervalS: 120,
31
- lastSyncedBlock: null
32
- }
14
+ const updateAddressLastSyncCommand = (db, {
15
+ lastSyncAt,
16
+ addressId,
17
+ lastSyncMetadata
18
+ }) => {
19
+ const command = db.update(drizzle.tables.address).set({
20
+ lastSyncAt,
21
+ lastSyncMetadata
22
+ }).where(drizzleOrm.eq(drizzle.tables.address.id, addressId)).returning();
23
+ return {
24
+ command
25
+ };
26
+ };
27
+
28
+ const getAddressById = async (db, id) => {
29
+ const address = await db.select().from(drizzle.tables.address).where(drizzleOrm.eq(drizzle.tables.address.id, id)).get();
30
+ return address;
31
+ };
32
+
33
+ const createTransactionCommand = (db, { transaction }) => {
34
+ const id = backendSdk.uuidv4();
35
+ const command = db.insert(drizzle.tables.transaction).values({ ...transaction, id }).returning();
36
+ return { command };
33
37
  };
38
+
39
+ function pushToQueue(queue, message) {
40
+ if (!Array.isArray(message)) return queue.send(message, { contentType: "v8" });
41
+ return queue.sendBatch(
42
+ message.map((m) => ({
43
+ body: m,
44
+ contentType: "v8"
45
+ }))
46
+ );
47
+ }
34
48
  class BlockchainSyncAddressTransactions extends cloudflare_workers.WorkflowEntrypoint {
35
49
  async run(event, step) {
36
50
  const { addressId } = event.payload;
51
+ const db = d1.drizzle(this.env.BLOCKCHAIN_D1, { schema: drizzle.tables });
52
+ if (!addressId) {
53
+ throw new cloudflare_workflows.NonRetryableError(`Haven't obtained addressId to load.`);
54
+ }
37
55
  while (true) {
56
+ const now = /* @__PURE__ */ new Date();
38
57
  const address = await step.do("load address", async () => {
39
- const address2 = TEST_ADDRESSES[addressId];
58
+ const address2 = await getAddressById(db, addressId);
40
59
  if (!address2) {
41
60
  throw new cloudflare_workflows.NonRetryableError(
42
61
  `Blockchain address not found: ${addressId}.`
@@ -44,32 +63,45 @@ class BlockchainSyncAddressTransactions extends cloudflare_workers.WorkflowEntry
44
63
  }
45
64
  return address2;
46
65
  });
47
- await step.do(
66
+ if (!address.lastSyncAt) {
67
+ throw new Error(`lastSyncedAt is not set for address: ${address}`);
68
+ }
69
+ const transactions = await step.do(
48
70
  "fetch address transactions",
49
71
  {
50
72
  retries: { limit: 5, delay: 2e3, backoff: "constant" },
51
73
  timeout: "30 seconds"
52
74
  },
53
75
  async () => {
76
+ const rpcUrl = (await this.env.SECRETS_STORE.get({
77
+ secretName: "BLOCKCHAIN_SERVICE_RPC_URL"
78
+ })).data?.secretValue || "";
54
79
  const client = viem.createPublicClient({
55
- chain: chains.sepolia,
56
- transport: viem.http()
80
+ chain: chains.gnosisChiado,
81
+ transport: viem.http(rpcUrl)
57
82
  });
58
83
  const currentBlock = await client.getBlockNumber();
59
- const startBlock = address.lastSyncedBlock ? BigInt(address.lastSyncedBlock) + BigInt(1) : currentBlock - BigInt(10);
60
- const endBlock = currentBlock;
61
- console.log(
62
- `\u{1F50D} Scanning blocks ${startBlock} to ${endBlock} for address ${address.address}`
84
+ const estimatedBlocksPerInterval = Math.ceil(
85
+ address.syncIntervalS * 1.5 / 5
63
86
  );
87
+ const blocksToScan = Math.min(estimatedBlocksPerInterval, 1e3);
88
+ const startBlock = currentBlock - BigInt(blocksToScan);
89
+ const endBlock = currentBlock;
90
+ const lastSyncedTimestamp = address.lastSyncAt || 0;
64
91
  const allTransactions = [];
65
92
  for (let blockNum = startBlock; blockNum <= endBlock; blockNum++) {
66
93
  const block = await client.getBlock({
67
94
  blockNumber: blockNum,
68
95
  includeTransactions: true
69
96
  });
97
+ const blockTimestamp = Number(block.timestamp);
98
+ const lastSyncedTimestampNum = typeof lastSyncedTimestamp === "number" ? lastSyncedTimestamp : Number(lastSyncedTimestamp);
99
+ if (blockTimestamp <= lastSyncedTimestampNum) {
100
+ continue;
101
+ }
70
102
  for (const tx of block.transactions) {
71
- if (typeof tx === "string") continue;
72
- if (tx.from.toLowerCase() === address.address.toLowerCase() || tx.to?.toLowerCase() === address.address.toLowerCase()) {
103
+ if (typeof tx === "string" || !tx.to) continue;
104
+ if (tx.from.toLowerCase() === address.number.toLowerCase()) {
73
105
  const receipt = await client.getTransactionReceipt({
74
106
  hash: tx.hash
75
107
  });
@@ -81,23 +113,70 @@ class BlockchainSyncAddressTransactions extends cloudflare_workers.WorkflowEntry
81
113
  blockNumber: block.number.toString(),
82
114
  blockHash: block.hash,
83
115
  gasUsed: receipt.gasUsed.toString(),
84
- gasPrice: tx.gasPrice?.toString(),
85
- timestamp: Number(block.timestamp),
86
- status: receipt.status === "success" ? "success" : "reverted"
116
+ gasPrice: tx.gasPrice?.toString() ?? "",
117
+ timestamp: blockTimestamp,
118
+ status: receipt.status
87
119
  });
88
120
  }
89
121
  }
90
122
  }
91
- console.log(`\u2705 Sync complete for ${address.address}`);
92
- console.log(` Found ${allTransactions.length} transactions`);
93
- console.log(` Blocks scanned: ${startBlock} to ${endBlock}`);
94
- if (allTransactions.length > 0) {
95
- console.log("Transactions:");
96
- console.log(JSON.stringify(allTransactions));
97
- }
98
123
  return allTransactions;
99
124
  }
100
125
  );
126
+ if (transactions.length > 0) {
127
+ await step.do(
128
+ "process new transactions and update lastSyncAt",
129
+ async () => {
130
+ const createCommands = transactions.map(
131
+ (tx) => createTransactionCommand(db, { transaction: tx }).command
132
+ );
133
+ const eventsToEmit = transactions.map(
134
+ (tx) => ({
135
+ eventType: "BLOCKCHAIN_TRANSACTION",
136
+ blockchainTransaction: {
137
+ address: address.number,
138
+ hash: tx.hash,
139
+ from: tx.from,
140
+ to: tx.to,
141
+ value: tx.value,
142
+ blockNumber: tx.blockNumber,
143
+ blockHash: tx.blockHash,
144
+ gasUsed: tx.gasUsed,
145
+ gasPrice: tx.gasPrice,
146
+ timestamp: tx.timestamp,
147
+ status: tx.status
148
+ },
149
+ metadata: {
150
+ correlationId: backendSdk.uuidv4(),
151
+ entityId: backendSdk.uuidv4(),
152
+ timestamp: (/* @__PURE__ */ new Date()).toDateString()
153
+ }
154
+ })
155
+ );
156
+ const lastSyncMetadata = {
157
+ eventsEmitted: eventsToEmit.length
158
+ };
159
+ const updateLastSyncCommand = updateAddressLastSyncCommand(db, {
160
+ addressId: address.id,
161
+ lastSyncAt: now,
162
+ lastSyncMetadata
163
+ }).command;
164
+ if (eventsToEmit.length) {
165
+ await db.batch(
166
+ backendSdk.asNonEmpty([updateLastSyncCommand, ...createCommands])
167
+ );
168
+ await pushToQueue(
169
+ this.env.QUEUE_BUS_QUEUE,
170
+ eventsToEmit
171
+ );
172
+ }
173
+ return {
174
+ ...lastSyncMetadata,
175
+ newLastSyncAt: now
176
+ };
177
+ }
178
+ );
179
+ }
101
180
  await step.sleep(
102
181
  "sleep until next sync",
103
182
  `${address.syncIntervalS} seconds`