@pimlico/alto 0.0.0-main.20250916T162729 → 0.0.0-main.20250930T125129
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/esm/cli/config/bundler.d.ts +12 -0
- package/esm/cli/config/bundler.js +2 -0
- package/esm/cli/config/bundler.js.map +1 -1
- package/esm/cli/config/options.js +11 -0
- package/esm/cli/config/options.js.map +1 -1
- package/esm/cli/setupServer.js +1 -1
- package/esm/cli/setupServer.js.map +1 -1
- package/esm/cli/shutDown.js +15 -55
- package/esm/cli/shutDown.js.map +1 -1
- package/esm/executor/bundleManager.d.ts +1 -1
- package/esm/executor/bundleManager.js +47 -34
- package/esm/executor/bundleManager.js.map +1 -1
- package/esm/executor/executorManager.js +3 -4
- package/esm/executor/executorManager.js.map +1 -1
- package/esm/executor/senderManager/createRedisSenderManager.js +1 -2
- package/esm/executor/senderManager/createRedisSenderManager.js.map +1 -1
- package/esm/handlers/arbitrumGasPriceManager.js +2 -2
- package/esm/handlers/gasPriceManager.js +3 -3
- package/esm/handlers/mantleGasPriceManager.js +4 -4
- package/esm/handlers/optimismManager.js +1 -1
- package/esm/mempool/mempool.d.ts +3 -11
- package/esm/mempool/mempool.js +91 -93
- package/esm/mempool/mempool.js.map +1 -1
- package/esm/mempool/monitoring.d.ts +1 -1
- package/esm/mempool/monitoring.js +30 -21
- package/esm/mempool/monitoring.js.map +1 -1
- package/esm/receiptCache/createMemoryReceiptCache.js +8 -5
- package/esm/receiptCache/createMemoryReceiptCache.js.map +1 -1
- package/esm/receiptCache/createRedisReceiptCache.js +13 -11
- package/esm/receiptCache/createRedisReceiptCache.js.map +1 -1
- package/esm/receiptCache/index.d.ts +4 -1
- package/esm/receiptCache/index.js.map +1 -1
- package/esm/rpc/methods/pimlico_sendUserOperationNow.js +4 -1
- package/esm/rpc/methods/pimlico_sendUserOperationNow.js.map +1 -1
- package/esm/store/createMempoolStore.d.ts +1 -1
- package/esm/store/createMempoolStore.js +51 -143
- package/esm/store/createMempoolStore.js.map +1 -1
- package/esm/store/index.d.ts +4 -75
- package/esm/store/index.js +4 -1
- package/esm/store/index.js.map +1 -1
- package/esm/store/outstanding/index.d.ts +13 -0
- package/esm/store/outstanding/index.js +16 -0
- package/esm/store/outstanding/index.js.map +1 -0
- package/esm/store/{createMemoryOutstandingStore.d.ts → outstanding/memory.d.ts} +11 -10
- package/esm/store/outstanding/memory.js +249 -0
- package/esm/store/outstanding/memory.js.map +1 -0
- package/esm/store/outstanding/redis.d.ts +11 -0
- package/esm/store/outstanding/redis.js +201 -0
- package/esm/store/outstanding/redis.js.map +1 -0
- package/esm/store/outstanding/types.d.ts +18 -0
- package/esm/store/outstanding/types.js +2 -0
- package/esm/store/outstanding/types.js.map +1 -0
- package/esm/store/processing/index.d.ts +11 -0
- package/esm/store/processing/index.js +17 -0
- package/esm/store/processing/index.js.map +1 -0
- package/esm/store/processing/memory.d.ts +14 -0
- package/esm/store/processing/memory.js +46 -0
- package/esm/store/processing/memory.js.map +1 -0
- package/esm/store/processing/redis.d.ts +21 -0
- package/esm/store/processing/redis.js +69 -0
- package/esm/store/processing/redis.js.map +1 -0
- package/esm/store/processing/types.d.ts +9 -0
- package/esm/store/processing/types.js +2 -0
- package/esm/store/processing/types.js.map +1 -0
- package/esm/store/types.d.ts +49 -0
- package/esm/store/types.js +2 -0
- package/esm/store/types.js.map +1 -0
- package/esm/utils/minMaxQueue/createRedisMinMaxQueue.d.ts +23 -2
- package/esm/utils/minMaxQueue/createRedisMinMaxQueue.js +83 -60
- package/esm/utils/minMaxQueue/createRedisMinMaxQueue.js.map +1 -1
- package/esm/utils/minMaxQueue/index.d.ts +2 -2
- package/esm/utils/minMaxQueue/index.js +2 -2
- package/esm/utils/minMaxQueue/index.js.map +1 -1
- package/esm/utils/userop.d.ts +1 -0
- package/esm/utils/userop.js +6 -0
- package/esm/utils/userop.js.map +1 -1
- package/package.json +1 -1
- package/esm/cli/config/redisKeys.d.ts +0 -9
- package/esm/cli/config/redisKeys.js +0 -16
- package/esm/cli/config/redisKeys.js.map +0 -1
- package/esm/store/createMemoryOutstandingStore.js +0 -240
- package/esm/store/createMemoryOutstandingStore.js.map +0 -1
- package/esm/store/createRedisOutstandingStore.d.ts +0 -28
- package/esm/store/createRedisOutstandingStore.js +0 -341
- package/esm/store/createRedisOutstandingStore.js.map +0 -1
- package/esm/store/createRedisStore.d.ts +0 -10
- package/esm/store/createRedisStore.js +0 -128
- package/esm/store/createRedisStore.js.map +0 -1
- package/esm/store/createStore.d.ts +0 -6
- package/esm/store/createStore.js +0 -66
- package/esm/store/createStore.js.map +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { userOpInfoSchema } from "../../types/index.js";
|
|
2
|
+
import { getNonceKeyAndSequence, isDeployment } from "../../utils/index.js";
|
|
3
|
+
import { Redis } from "ioredis";
|
|
4
|
+
import { toHex } from "viem/utils";
|
|
5
|
+
const serialize = (userOpInfo) => {
|
|
6
|
+
return JSON.stringify(userOpInfo, (_, value) => typeof value === "bigint" ? toHex(value) : value);
|
|
7
|
+
};
|
|
8
|
+
const deserialize = (data) => {
|
|
9
|
+
const parsed = JSON.parse(data);
|
|
10
|
+
return userOpInfoSchema.parse(parsed);
|
|
11
|
+
};
|
|
12
|
+
class RedisOutstandingQueue {
|
|
13
|
+
redis;
|
|
14
|
+
senderNonceKeyPrefix; // sender + nonceKey -> userOpHash
|
|
15
|
+
readyQueue; // Queue of userOpHashes (sorted by composite of userOp.nonceSeq + userOp.maxFeePerGas)
|
|
16
|
+
userOpHashMap; // userOpHash -> boolean
|
|
17
|
+
deploymentHashMap; // sender -> deployment userOpHash
|
|
18
|
+
senderNonceQueueTtl; // TTL for sender nonce queues in seconds
|
|
19
|
+
constructor({ config, entryPoint, redisEndpoint, logger }) {
|
|
20
|
+
this.redis = new Redis(redisEndpoint, {});
|
|
21
|
+
// Initialize Redis key names
|
|
22
|
+
const redisPrefix = `${config.redisKeyPrefix}:${config.chainId}:${entryPoint}:outstanding`;
|
|
23
|
+
this.readyQueue = `${redisPrefix}:ready-queue`;
|
|
24
|
+
this.senderNonceKeyPrefix = `${redisPrefix}:sender`;
|
|
25
|
+
this.userOpHashMap = `${redisPrefix}:userop-hash`;
|
|
26
|
+
this.deploymentHashMap = `${redisPrefix}:deployment-senders`;
|
|
27
|
+
// Calculate TTL for sender nonce queues (10 blocks worth of time)
|
|
28
|
+
this.senderNonceQueueTtl = config.blockTime * 10;
|
|
29
|
+
logger.info({
|
|
30
|
+
readyQueueKey: this.readyQueue
|
|
31
|
+
}, "Using redis for outstanding mempool.");
|
|
32
|
+
}
|
|
33
|
+
// Returns queue index for this sender + nonceKey.
|
|
34
|
+
getSenderNonceQueue(userOp) {
|
|
35
|
+
const [nonceKey] = getNonceKeyAndSequence(userOp.nonce);
|
|
36
|
+
return `${this.senderNonceKeyPrefix}:${userOp.sender}:${nonceKey}`;
|
|
37
|
+
}
|
|
38
|
+
// OutstandingStore methods
|
|
39
|
+
async contains(userOpHash) {
|
|
40
|
+
const exists = await this.redis.hexists(this.userOpHashMap, userOpHash);
|
|
41
|
+
return exists === 1;
|
|
42
|
+
}
|
|
43
|
+
async popConflicting(userOp) {
|
|
44
|
+
const { sender, nonce } = userOp;
|
|
45
|
+
const [nonceSeq] = getNonceKeyAndSequence(nonce);
|
|
46
|
+
// Check for conflicting nonce.
|
|
47
|
+
const senderNonceQueue = this.getSenderNonceQueue(userOp);
|
|
48
|
+
const conflictingHashes = await this.redis.zrangebyscore(senderNonceQueue, Number(nonceSeq), Number(nonceSeq));
|
|
49
|
+
if (conflictingHashes.length > 0) {
|
|
50
|
+
const conflictingHash = conflictingHashes[0];
|
|
51
|
+
const removedUserOps = await this.remove([conflictingHash]);
|
|
52
|
+
if (removedUserOps.length > 0) {
|
|
53
|
+
return {
|
|
54
|
+
reason: "conflicting_nonce",
|
|
55
|
+
userOpInfo: removedUserOps[0]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Check for conflicting deployment.
|
|
60
|
+
if (isDeployment(userOp)) {
|
|
61
|
+
const existingDeploymentHash = (await this.redis.hget(this.deploymentHashMap, sender));
|
|
62
|
+
if (existingDeploymentHash) {
|
|
63
|
+
const removedUserOps = await this.remove([
|
|
64
|
+
existingDeploymentHash
|
|
65
|
+
]);
|
|
66
|
+
if (removedUserOps.length > 0) {
|
|
67
|
+
return {
|
|
68
|
+
reason: "conflicting_deployment",
|
|
69
|
+
userOpInfo: removedUserOps[0]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
async add(userOpInfos) {
|
|
77
|
+
if (userOpInfos.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
// Use pipeline for atomic operations
|
|
80
|
+
const pipeline = this.redis.pipeline();
|
|
81
|
+
for (const userOpInfo of userOpInfos) {
|
|
82
|
+
const { userOp } = userOpInfo;
|
|
83
|
+
const [, nonceSequence] = getNonceKeyAndSequence(userOp.nonce);
|
|
84
|
+
const userOpHash = userOpInfo.userOpHash;
|
|
85
|
+
// Create composite score: nonceSequence in upper 32 bits, maxFeePerGas in lower 32 bits.
|
|
86
|
+
const score = (nonceSequence << 32n) | (userOp.maxFeePerGas & 0xffffffffn);
|
|
87
|
+
// Serialize userOpInfo for storage.
|
|
88
|
+
const serializedUserOp = serialize(userOpInfo);
|
|
89
|
+
// Add to main ready queue.
|
|
90
|
+
pipeline.zadd(this.readyQueue, Number(score), userOpHash);
|
|
91
|
+
// Add to sender+nonceKey index for fast lookup.
|
|
92
|
+
const senderNonceQueue = this.getSenderNonceQueue(userOp);
|
|
93
|
+
pipeline.zadd(senderNonceQueue, Number(nonceSequence), userOpHash);
|
|
94
|
+
pipeline.expire(senderNonceQueue, this.senderNonceQueueTtl);
|
|
95
|
+
// Add to userOpHash map.
|
|
96
|
+
pipeline.hset(this.userOpHashMap, userOpHash, serializedUserOp);
|
|
97
|
+
// Add sender to deployment hash if this is a deployment.
|
|
98
|
+
if (isDeployment(userOp)) {
|
|
99
|
+
pipeline.hset(this.deploymentHashMap, userOp.sender, userOpHash);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
await pipeline.exec();
|
|
103
|
+
}
|
|
104
|
+
// Removes userOps given their hashes and returns the userOpInfo objects.
|
|
105
|
+
async remove(userOpHashes) {
|
|
106
|
+
if (userOpHashes.length === 0)
|
|
107
|
+
return [];
|
|
108
|
+
// Get all serialized data in parallel.
|
|
109
|
+
const pipeline = this.redis.pipeline();
|
|
110
|
+
for (const hash of userOpHashes) {
|
|
111
|
+
pipeline.hget(this.userOpHashMap, hash);
|
|
112
|
+
}
|
|
113
|
+
const serializedResults = await pipeline.exec();
|
|
114
|
+
const removedUserOps = [];
|
|
115
|
+
const removalPipeline = this.redis.pipeline();
|
|
116
|
+
// Process each userOp that exists.
|
|
117
|
+
for (let i = 0; i < userOpHashes.length; i++) {
|
|
118
|
+
const serialized = serializedResults?.[i]?.[1];
|
|
119
|
+
if (!serialized)
|
|
120
|
+
continue;
|
|
121
|
+
const userOpHash = userOpHashes[i];
|
|
122
|
+
const userOpInfo = deserialize(serialized);
|
|
123
|
+
const { userOp } = userOpInfo;
|
|
124
|
+
const [, nonceSequence] = getNonceKeyAndSequence(userOp.nonce);
|
|
125
|
+
const senderNonceQueue = this.getSenderNonceQueue(userOp);
|
|
126
|
+
const isDeploymentOp = isDeployment(userOp);
|
|
127
|
+
// Remove from ready queue.
|
|
128
|
+
removalPipeline.zrem(this.readyQueue, userOpHash);
|
|
129
|
+
// Remove from sender nonceKey queue.
|
|
130
|
+
removalPipeline.zremrangebyscore(senderNonceQueue, Number(nonceSequence), Number(nonceSequence));
|
|
131
|
+
// Remove from hash lookup.
|
|
132
|
+
removalPipeline.hdel(this.userOpHashMap, userOpHash);
|
|
133
|
+
// Remove sender from deployment hash if this was a deployment.
|
|
134
|
+
if (isDeploymentOp) {
|
|
135
|
+
removalPipeline.hdel(this.deploymentHashMap, userOp.sender);
|
|
136
|
+
}
|
|
137
|
+
removedUserOps.push(userOpInfo);
|
|
138
|
+
}
|
|
139
|
+
// Execute all removals.
|
|
140
|
+
await removalPipeline.exec();
|
|
141
|
+
return removedUserOps;
|
|
142
|
+
}
|
|
143
|
+
async pop(count) {
|
|
144
|
+
// Pop from ready queue
|
|
145
|
+
const results = await this.redis.zpopmax(this.readyQueue, count);
|
|
146
|
+
if (!results || results.length === 0) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
// Extract hashes from results (zpopmax returns [member, score, member, score, ...])
|
|
150
|
+
const userOpHashes = [];
|
|
151
|
+
for (let i = 0; i < results.length; i += 2) {
|
|
152
|
+
userOpHashes.push(results[i]);
|
|
153
|
+
}
|
|
154
|
+
// Remove all userOps in batch
|
|
155
|
+
return await this.remove(userOpHashes);
|
|
156
|
+
}
|
|
157
|
+
async getQueuedUserOps(userOp) {
|
|
158
|
+
const [, nonceSequence] = getNonceKeyAndSequence(userOp.nonce);
|
|
159
|
+
const senderNonceQueue = this.getSenderNonceQueue(userOp);
|
|
160
|
+
// Get all userOpHashes with nonce sequence lower than the input
|
|
161
|
+
// ZRANGEBYSCORE returns elements with scores between min and max
|
|
162
|
+
// We want scores from 0 to (nonceSequence - 1)
|
|
163
|
+
const userOpHashes = await this.redis.zrangebyscore(senderNonceQueue, 0, Number(nonceSequence) - 1);
|
|
164
|
+
if (userOpHashes.length === 0)
|
|
165
|
+
return [];
|
|
166
|
+
// Fetch serialized data for each hash
|
|
167
|
+
const pipeline = this.redis.pipeline();
|
|
168
|
+
for (const hash of userOpHashes) {
|
|
169
|
+
pipeline.hget(this.userOpHashMap, hash);
|
|
170
|
+
}
|
|
171
|
+
const results = await pipeline.exec();
|
|
172
|
+
// Deserialize and extract userOps
|
|
173
|
+
return (results || [])
|
|
174
|
+
.map((result) => result?.[1])
|
|
175
|
+
.filter((serialized) => serialized !== null)
|
|
176
|
+
.map((serialized) => deserialize(serialized).userOp);
|
|
177
|
+
}
|
|
178
|
+
// These methods aren't implemented
|
|
179
|
+
dumpLocal() {
|
|
180
|
+
return Promise.resolve([]); // We can't dump from redis as the latency is too high
|
|
181
|
+
}
|
|
182
|
+
clear() {
|
|
183
|
+
throw new Error("Not implemented: clear");
|
|
184
|
+
}
|
|
185
|
+
// Skip limit checks when using Redis
|
|
186
|
+
validateQueuedLimit() {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
validateParallelLimit() {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
export const createRedisOutstandingQueue = ({ config, entryPoint, redisEndpoint, logger }) => {
|
|
194
|
+
return new RedisOutstandingQueue({
|
|
195
|
+
config,
|
|
196
|
+
entryPoint,
|
|
197
|
+
redisEndpoint,
|
|
198
|
+
logger
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../../store/outstanding/redis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { HexData32, UserOpInfo, UserOperation } from "../../types/index.js";
|
|
2
|
+
export type ConflictingOutstandingType = {
|
|
3
|
+
reason: "conflicting_nonce" | "conflicting_deployment";
|
|
4
|
+
userOpInfo: UserOpInfo;
|
|
5
|
+
} | undefined;
|
|
6
|
+
export interface OutstandingStore {
|
|
7
|
+
contains(userOpHash: HexData32): Promise<boolean>;
|
|
8
|
+
pop(count: number): Promise<UserOpInfo[]>;
|
|
9
|
+
popConflicting(userOp: UserOperation): Promise<ConflictingOutstandingType>;
|
|
10
|
+
add(userOpInfos: UserOpInfo[]): Promise<void>;
|
|
11
|
+
remove(userOpHash: HexData32[]): Promise<UserOpInfo[]>;
|
|
12
|
+
getQueuedUserOps(userOp: UserOperation): Promise<UserOperation[]>;
|
|
13
|
+
dumpLocal(): Promise<UserOpInfo[]>;
|
|
14
|
+
clear(): Promise<void>;
|
|
15
|
+
validateQueuedLimit(userOp: UserOperation): boolean;
|
|
16
|
+
validateParallelLimit(userOp: UserOperation): boolean;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../store/outstanding/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
import type { AltoConfig } from "../../createConfig.js";
|
|
3
|
+
import { type ProcessingStore } from "../index.js";
|
|
4
|
+
export declare function createProcessingStore({ config, entryPoint }: {
|
|
5
|
+
config: AltoConfig;
|
|
6
|
+
entryPoint: Address;
|
|
7
|
+
}): ProcessingStore;
|
|
8
|
+
export * from "./types.js";
|
|
9
|
+
export * from "./memory.js";
|
|
10
|
+
export * from "./redis.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { InMemoryProcessingStore, RedisProcessingStore } from "../index.js";
|
|
2
|
+
// Holds all userOps that have been removed from outstanding pool and are being processed.
|
|
3
|
+
// UserOps are are removed from this store when they have successfully landed onchain or when they are cancelled.
|
|
4
|
+
export function createProcessingStore({ config, entryPoint }) {
|
|
5
|
+
if (config.enableHorizontalScaling && config.redisEndpoint) {
|
|
6
|
+
return new RedisProcessingStore({
|
|
7
|
+
config,
|
|
8
|
+
entryPoint,
|
|
9
|
+
redisEndpoint: config.redisEndpoint
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return new InMemoryProcessingStore();
|
|
13
|
+
}
|
|
14
|
+
export * from "./types.js";
|
|
15
|
+
export * from "./memory.js";
|
|
16
|
+
export * from "./redis.js";
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../store/processing/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,uBAAuB,EAEvB,oBAAoB,EACvB,MAAM,aAAa,CAAA;AAEpB,0FAA0F;AAC1F,iHAAiH;AACjH,MAAM,UAAU,qBAAqB,CAAC,EAClC,MAAM,EACN,UAAU,EAIb;IACG,IAAI,MAAM,CAAC,uBAAuB,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACzD,OAAO,IAAI,oBAAoB,CAAC;YAC5B,MAAM;YACN,UAAU;YACV,aAAa,EAAE,MAAM,CAAC,aAAa;SACtC,CAAC,CAAA;IACN,CAAC;IACD,OAAO,IAAI,uBAAuB,EAAE,CAAA;AACxC,CAAC;AAED,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { UserOpInfo, UserOperation } from "../../types/index.js";
|
|
2
|
+
import type { Hex } from "viem";
|
|
3
|
+
import type { ProcessingStore } from "./types.js";
|
|
4
|
+
export declare class InMemoryProcessingStore implements ProcessingStore {
|
|
5
|
+
private processingUserOpsSet;
|
|
6
|
+
private processingSenderNonceSet;
|
|
7
|
+
private processingDeploymentSet;
|
|
8
|
+
private encodeSenderNonceId;
|
|
9
|
+
addProcessing(userOpInfo: UserOpInfo): Promise<void>;
|
|
10
|
+
removeProcessing(userOpInfo: UserOpInfo): Promise<void>;
|
|
11
|
+
isProcessing(userOpHash: Hex): Promise<boolean>;
|
|
12
|
+
wouldConflict(userOp: UserOperation): Promise<"nonce_conflict" | "deployment_conflict" | undefined>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { isDeployment } from "../../utils/index.js";
|
|
2
|
+
export class InMemoryProcessingStore {
|
|
3
|
+
// Use Sets for boolean membership tracking (matching Redis store)
|
|
4
|
+
processingUserOpsSet = new Set();
|
|
5
|
+
processingSenderNonceSet = new Set();
|
|
6
|
+
processingDeploymentSet = new Set();
|
|
7
|
+
encodeSenderNonceId(sender, nonce) {
|
|
8
|
+
return `${sender}:${nonce}`;
|
|
9
|
+
}
|
|
10
|
+
async addProcessing(userOpInfo) {
|
|
11
|
+
const { userOpHash, userOp } = userOpInfo;
|
|
12
|
+
const isDeploymentOp = isDeployment(userOp);
|
|
13
|
+
const senderNonceId = this.encodeSenderNonceId(userOp.sender, userOp.nonce);
|
|
14
|
+
// Add to processing sets
|
|
15
|
+
this.processingUserOpsSet.add(userOpHash);
|
|
16
|
+
this.processingSenderNonceSet.add(senderNonceId);
|
|
17
|
+
if (isDeploymentOp) {
|
|
18
|
+
this.processingDeploymentSet.add(userOp.sender);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async removeProcessing(userOpInfo) {
|
|
22
|
+
const { userOpHash, userOp } = userOpInfo;
|
|
23
|
+
const senderNonceId = this.encodeSenderNonceId(userOp.sender, userOp.nonce);
|
|
24
|
+
// Remove from all sets
|
|
25
|
+
this.processingUserOpsSet.delete(userOpHash);
|
|
26
|
+
this.processingSenderNonceSet.delete(senderNonceId);
|
|
27
|
+
this.processingDeploymentSet.delete(userOp.sender);
|
|
28
|
+
}
|
|
29
|
+
async isProcessing(userOpHash) {
|
|
30
|
+
return this.processingUserOpsSet.has(userOpHash);
|
|
31
|
+
}
|
|
32
|
+
async wouldConflict(userOp) {
|
|
33
|
+
const isDeploymentOp = isDeployment(userOp);
|
|
34
|
+
const senderNonceId = this.encodeSenderNonceId(userOp.sender, userOp.nonce);
|
|
35
|
+
// Check deployment conflict first
|
|
36
|
+
if (isDeploymentOp && this.processingDeploymentSet.has(userOp.sender)) {
|
|
37
|
+
return "deployment_conflict";
|
|
38
|
+
}
|
|
39
|
+
// Check nonce conflict
|
|
40
|
+
if (this.processingSenderNonceSet.has(senderNonceId)) {
|
|
41
|
+
return "nonce_conflict";
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../store/processing/memory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI1C,MAAM,OAAO,uBAAuB;IAChC,kEAAkE;IAC1D,oBAAoB,GAAG,IAAI,GAAG,EAAO,CAAA;IACrC,wBAAwB,GAAG,IAAI,GAAG,EAAU,CAAA;IAC5C,uBAAuB,GAAG,IAAI,GAAG,EAAW,CAAA;IAE5C,mBAAmB,CAAC,MAAe,EAAE,KAAa;QACtD,OAAO,GAAG,MAAM,IAAI,KAAK,EAAE,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,UAAsB;QACtC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,UAAU,CAAA;QACzC,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAC1C,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,KAAK,CACf,CAAA;QAED,yBAAyB;QACzB,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QACzC,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;QAEhD,IAAI,cAAc,EAAE,CAAC;YACjB,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACnD,CAAC;IACL,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,UAAsB;QACzC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,UAAU,CAAA;QACzC,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAC1C,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,KAAK,CACf,CAAA;QAED,uBAAuB;QACvB,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;QAC5C,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QACnD,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACtD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,UAAe;QAC9B,OAAO,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACpD,CAAC;IAED,KAAK,CAAC,aAAa,CACf,MAAqB;QAErB,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAC1C,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,KAAK,CACf,CAAA;QAED,kCAAkC;QAClC,IAAI,cAAc,IAAI,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACpE,OAAO,qBAAqB,CAAA;QAChC,CAAC;QAED,uBAAuB;QACvB,IAAI,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YACnD,OAAO,gBAAgB,CAAA;QAC3B,CAAC;QAED,OAAO,SAAS,CAAA;IACpB,CAAC;CACJ"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ProcessingStore } from "../index.js";
|
|
2
|
+
import type { UserOpInfo, UserOperation } from "../../types/index.js";
|
|
3
|
+
import type { Address, Hex } from "viem";
|
|
4
|
+
import type { AltoConfig } from "../../createConfig.js";
|
|
5
|
+
export declare class RedisProcessingStore implements ProcessingStore {
|
|
6
|
+
private redis;
|
|
7
|
+
private processingUserOpsSet;
|
|
8
|
+
private processingSenderNonceSet;
|
|
9
|
+
private processingDeploymentSet;
|
|
10
|
+
private encodeSenderNonceId;
|
|
11
|
+
constructor({ config, entryPoint, redisEndpoint }: {
|
|
12
|
+
config: AltoConfig;
|
|
13
|
+
entryPoint: Address;
|
|
14
|
+
redisEndpoint: string;
|
|
15
|
+
});
|
|
16
|
+
addProcessing(userOpInfo: UserOpInfo): Promise<void>;
|
|
17
|
+
removeProcessing(userOpInfo: UserOpInfo): Promise<void>;
|
|
18
|
+
isProcessing(userOpHash: Hex): Promise<boolean>;
|
|
19
|
+
wouldConflict(userOp: UserOperation): Promise<"nonce_conflict" | "deployment_conflict" | undefined>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=redis.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { isDeployment } from "../../utils/index.js";
|
|
2
|
+
import { Redis } from "ioredis";
|
|
3
|
+
export class RedisProcessingStore {
|
|
4
|
+
redis;
|
|
5
|
+
processingUserOpsSet; // set of userOpHashes being processed
|
|
6
|
+
processingSenderNonceSet; // set of "sender:nonce" being processed
|
|
7
|
+
processingDeploymentSet; // set of senders with deployments being processed
|
|
8
|
+
encodeSenderNonceId(sender, nonce) {
|
|
9
|
+
return `${sender}:${nonce}`;
|
|
10
|
+
}
|
|
11
|
+
constructor({ config, entryPoint, redisEndpoint }) {
|
|
12
|
+
this.redis = new Redis(redisEndpoint);
|
|
13
|
+
const redisPrefix = `${config.redisKeyPrefix}:${config.chainId}:${entryPoint}:processing`;
|
|
14
|
+
this.processingUserOpsSet = `${redisPrefix}:userOps`;
|
|
15
|
+
this.processingSenderNonceSet = `${redisPrefix}:senderNonce`;
|
|
16
|
+
this.processingDeploymentSet = `${redisPrefix}:deployment`;
|
|
17
|
+
}
|
|
18
|
+
async addProcessing(userOpInfo) {
|
|
19
|
+
const { userOpHash, userOp } = userOpInfo;
|
|
20
|
+
const isDeploymentOp = isDeployment(userOp);
|
|
21
|
+
const senderNonceId = this.encodeSenderNonceId(userOp.sender, userOp.nonce);
|
|
22
|
+
// Use MULTI for atomic operation in single round trip
|
|
23
|
+
const multi = this.redis.multi();
|
|
24
|
+
multi.sadd(this.processingUserOpsSet, userOpHash);
|
|
25
|
+
multi.sadd(this.processingSenderNonceSet, senderNonceId);
|
|
26
|
+
if (isDeploymentOp) {
|
|
27
|
+
multi.sadd(this.processingDeploymentSet, userOp.sender);
|
|
28
|
+
}
|
|
29
|
+
await multi.exec();
|
|
30
|
+
}
|
|
31
|
+
async removeProcessing(userOpInfo) {
|
|
32
|
+
const { userOpHash, userOp } = userOpInfo;
|
|
33
|
+
const isDeploymentOp = isDeployment(userOp);
|
|
34
|
+
const senderNonceId = this.encodeSenderNonceId(userOp.sender, userOp.nonce);
|
|
35
|
+
// Use MULTI for atomic removal in single round trip
|
|
36
|
+
const multi = this.redis.multi();
|
|
37
|
+
multi.srem(this.processingUserOpsSet, userOpHash);
|
|
38
|
+
multi.srem(this.processingSenderNonceSet, senderNonceId);
|
|
39
|
+
if (isDeploymentOp) {
|
|
40
|
+
multi.srem(this.processingDeploymentSet, userOp.sender);
|
|
41
|
+
}
|
|
42
|
+
await multi.exec();
|
|
43
|
+
}
|
|
44
|
+
async isProcessing(userOpHash) {
|
|
45
|
+
const isMember = await this.redis.sismember(this.processingUserOpsSet, userOpHash);
|
|
46
|
+
return isMember === 1;
|
|
47
|
+
}
|
|
48
|
+
async wouldConflict(userOp) {
|
|
49
|
+
const senderNonceId = this.encodeSenderNonceId(userOp.sender, userOp.nonce);
|
|
50
|
+
// Use MULTI for atomic read in single round trip
|
|
51
|
+
const multi = this.redis.multi();
|
|
52
|
+
multi.sismember(this.processingDeploymentSet, userOp.sender);
|
|
53
|
+
multi.sismember(this.processingSenderNonceSet, senderNonceId);
|
|
54
|
+
const results = await multi.exec();
|
|
55
|
+
if (!results)
|
|
56
|
+
return undefined;
|
|
57
|
+
const [deploymentResult, nonceResult] = results;
|
|
58
|
+
const hasDeployment = deploymentResult?.[1] === 1;
|
|
59
|
+
const hasNonce = nonceResult?.[1] === 1;
|
|
60
|
+
if (hasDeployment) {
|
|
61
|
+
return "deployment_conflict";
|
|
62
|
+
}
|
|
63
|
+
if (hasNonce) {
|
|
64
|
+
return "nonce_conflict";
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../../store/processing/redis.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAI/B,MAAM,OAAO,oBAAoB;IACrB,KAAK,CAAO;IAEZ,oBAAoB,CAAQ,CAAC,sCAAsC;IACnE,wBAAwB,CAAQ,CAAC,wCAAwC;IACzE,uBAAuB,CAAQ,CAAC,kDAAkD;IAElF,mBAAmB,CAAC,MAAe,EAAE,KAAa;QACtD,OAAO,GAAG,MAAM,IAAI,KAAK,EAAE,CAAA;IAC/B,CAAC;IAED,YAAY,EACR,MAAM,EACN,UAAU,EACV,aAAa,EAKhB;QACG,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,aAAa,CAAC,CAAA;QAErC,MAAM,WAAW,GAAG,GAAG,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,OAAO,IAAI,UAAU,aAAa,CAAA;QACzF,IAAI,CAAC,oBAAoB,GAAG,GAAG,WAAW,UAAU,CAAA;QACpD,IAAI,CAAC,wBAAwB,GAAG,GAAG,WAAW,cAAc,CAAA;QAC5D,IAAI,CAAC,uBAAuB,GAAG,GAAG,WAAW,aAAa,CAAA;IAC9D,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,UAAsB;QACtC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,UAAU,CAAA;QACzC,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAC1C,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,KAAK,CACf,CAAA;QAED,sDAAsD;QACtD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAA;QACjD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,aAAa,CAAC,CAAA;QAExD,IAAI,cAAc,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;QAC3D,CAAC;QAED,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,UAAsB;QACzC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,UAAU,CAAA;QACzC,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAC1C,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,KAAK,CACf,CAAA;QAED,oDAAoD;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAA;QACjD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,aAAa,CAAC,CAAA;QAExD,IAAI,cAAc,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;QAC3D,CAAC;QAED,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,UAAe;QAC9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CACvC,IAAI,CAAC,oBAAoB,EACzB,UAAU,CACb,CAAA;QACD,OAAO,QAAQ,KAAK,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,aAAa,CACf,MAAqB;QAErB,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAC1C,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,KAAK,CACf,CAAA;QAED,iDAAiD;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAChC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;QAC5D,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,wBAAwB,EAAE,aAAa,CAAC,CAAA;QAE7D,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;QAClC,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAA;QAE9B,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC,GAAG,OAAO,CAAA;QAC/C,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QACjD,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QAEvC,IAAI,aAAa,EAAE,CAAC;YAChB,OAAO,qBAAqB,CAAA;QAChC,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACX,OAAO,gBAAgB,CAAA;QAC3B,CAAC;QAED,OAAO,SAAS,CAAA;IACpB,CAAC;CACJ"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { UserOpInfo, UserOperation } from "../../types/index.js";
|
|
2
|
+
import type { Hex } from "viem";
|
|
3
|
+
export interface ProcessingStore {
|
|
4
|
+
addProcessing(userOp: UserOpInfo): Promise<void>;
|
|
5
|
+
removeProcessing(userOp: UserOpInfo): Promise<void>;
|
|
6
|
+
isProcessing(userOpHash: Hex): Promise<boolean>;
|
|
7
|
+
wouldConflict(userOp: UserOperation): Promise<"nonce_conflict" | "deployment_conflict" | undefined>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../store/processing/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Address } from "abitype";
|
|
2
|
+
import type { HexData32, UserOpInfo, UserOperation } from "../types/schemas.js";
|
|
3
|
+
import type { ConflictingOutstandingType } from "./outstanding/index.js";
|
|
4
|
+
export type StoreType = "outstanding" | "processing";
|
|
5
|
+
export type ValidationResult = {
|
|
6
|
+
valid: true;
|
|
7
|
+
} | {
|
|
8
|
+
valid: false;
|
|
9
|
+
reason: string;
|
|
10
|
+
};
|
|
11
|
+
export type EntryPointUserOpInfoParam = {
|
|
12
|
+
entryPoint: Address;
|
|
13
|
+
userOpInfo: UserOpInfo;
|
|
14
|
+
};
|
|
15
|
+
export type EntryPointUserOpInfosParam = {
|
|
16
|
+
entryPoint: Address;
|
|
17
|
+
userOpInfos: UserOpInfo[];
|
|
18
|
+
};
|
|
19
|
+
export type EntryPointUserOpHashParam = {
|
|
20
|
+
entryPoint: Address;
|
|
21
|
+
userOpHash: HexData32;
|
|
22
|
+
};
|
|
23
|
+
export type MempoolStore = {
|
|
24
|
+
popOutstanding: (entryPoint: Address, count: number) => Promise<UserOpInfo[]>;
|
|
25
|
+
addOutstanding: (args: EntryPointUserOpInfosParam) => Promise<void>;
|
|
26
|
+
removeOutstanding: (args: EntryPointUserOpHashParam) => Promise<void>;
|
|
27
|
+
dumpOutstanding: (entryPoint: Address) => Promise<UserOpInfo[]>;
|
|
28
|
+
addProcessing: (args: EntryPointUserOpInfoParam) => Promise<void>;
|
|
29
|
+
removeProcessing: (args: EntryPointUserOpInfoParam) => Promise<void>;
|
|
30
|
+
checkDuplicatesAndConflicts: (args: {
|
|
31
|
+
entryPoint: Address;
|
|
32
|
+
userOp: UserOperation;
|
|
33
|
+
userOpHash: HexData32;
|
|
34
|
+
}) => Promise<ValidationResult>;
|
|
35
|
+
popConflictingOustanding: (args: {
|
|
36
|
+
entryPoint: Address;
|
|
37
|
+
userOp: UserOperation;
|
|
38
|
+
}) => Promise<ConflictingOutstandingType>;
|
|
39
|
+
validateSenderLimits: (args: {
|
|
40
|
+
entryPoint: Address;
|
|
41
|
+
userOp: UserOperation;
|
|
42
|
+
}) => Promise<ValidationResult>;
|
|
43
|
+
getQueuedOutstandingUserOps: (args: {
|
|
44
|
+
userOp: UserOperation;
|
|
45
|
+
entryPoint: Address;
|
|
46
|
+
}) => Promise<UserOperation[]>;
|
|
47
|
+
clearOutstanding: (entryPoint: Address) => Promise<void>;
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../store/types.ts"],"names":[],"mappings":""}
|
|
@@ -1,8 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MinMaxQueue implementation in Redis using a single sorted set
|
|
3
|
+
*
|
|
4
|
+
* ## Data Structure
|
|
5
|
+
* - Uses a Redis sorted set where:
|
|
6
|
+
* - Score: timestamp when value was added (seconds since epoch)
|
|
7
|
+
* - Member: the actual bigint value (stored as string)
|
|
8
|
+
*
|
|
9
|
+
* ## Flow
|
|
10
|
+
* 1. **Adding values**: New values are added/updated with current timestamp as score
|
|
11
|
+
* 2. **Reading values**: Queries filter out expired entries based on timestamp
|
|
12
|
+
* 3. **Cleanup**: Background process runs every minute to remove expired entries
|
|
13
|
+
*
|
|
14
|
+
* ## Optimization Strategy
|
|
15
|
+
* - Lazy cleanup: expired entries filtered on read, bulk removed by background process (removing is expensive)
|
|
16
|
+
*
|
|
17
|
+
* ## TTL Management
|
|
18
|
+
* - Entries expire based on `queueValidity` (configured via `gasPriceExpiry`)
|
|
19
|
+
* - Background cleanup prevents unbounded growth
|
|
20
|
+
* - Read operations ignore expired entries without removing them
|
|
21
|
+
*/
|
|
1
22
|
import type { MinMaxQueue } from "./index.js";
|
|
2
23
|
import type { AltoConfig } from "../../createConfig.js";
|
|
3
|
-
export declare const createRedisMinMaxQueue: ({ config,
|
|
24
|
+
export declare const createRedisMinMaxQueue: ({ config, queueName, redisEndpoint }: {
|
|
4
25
|
config: AltoConfig;
|
|
5
|
-
|
|
26
|
+
queueName: string;
|
|
6
27
|
redisEndpoint: string;
|
|
7
28
|
}) => MinMaxQueue;
|
|
8
29
|
//# sourceMappingURL=createRedisMinMaxQueue.d.ts.map
|