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