@hyperlane-xyz/rebalancer-sim 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +195 -0
- package/README.md +582 -0
- package/dist/BridgeMockController.d.ts +87 -0
- package/dist/BridgeMockController.d.ts.map +1 -0
- package/dist/BridgeMockController.js +300 -0
- package/dist/BridgeMockController.js.map +1 -0
- package/dist/KPICollector.d.ts +81 -0
- package/dist/KPICollector.d.ts.map +1 -0
- package/dist/KPICollector.js +239 -0
- package/dist/KPICollector.js.map +1 -0
- package/dist/MessageTracker.d.ts +82 -0
- package/dist/MessageTracker.d.ts.map +1 -0
- package/dist/MessageTracker.js +213 -0
- package/dist/MessageTracker.js.map +1 -0
- package/dist/RebalancerSimulationHarness.d.ts +72 -0
- package/dist/RebalancerSimulationHarness.d.ts.map +1 -0
- package/dist/RebalancerSimulationHarness.js +217 -0
- package/dist/RebalancerSimulationHarness.js.map +1 -0
- package/dist/ScenarioGenerator.d.ts +50 -0
- package/dist/ScenarioGenerator.d.ts.map +1 -0
- package/dist/ScenarioGenerator.js +326 -0
- package/dist/ScenarioGenerator.js.map +1 -0
- package/dist/ScenarioLoader.d.ts +18 -0
- package/dist/ScenarioLoader.d.ts.map +1 -0
- package/dist/ScenarioLoader.js +59 -0
- package/dist/ScenarioLoader.js.map +1 -0
- package/dist/SimulationDeployment.d.ts +20 -0
- package/dist/SimulationDeployment.d.ts.map +1 -0
- package/dist/SimulationDeployment.js +170 -0
- package/dist/SimulationDeployment.js.map +1 -0
- package/dist/SimulationEngine.d.ts +58 -0
- package/dist/SimulationEngine.d.ts.map +1 -0
- package/dist/SimulationEngine.js +302 -0
- package/dist/SimulationEngine.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/runners/NoOpRebalancer.d.ts +17 -0
- package/dist/runners/NoOpRebalancer.d.ts.map +1 -0
- package/dist/runners/NoOpRebalancer.js +28 -0
- package/dist/runners/NoOpRebalancer.js.map +1 -0
- package/dist/runners/ProductionRebalancerRunner.d.ts +22 -0
- package/dist/runners/ProductionRebalancerRunner.d.ts.map +1 -0
- package/dist/runners/ProductionRebalancerRunner.js +219 -0
- package/dist/runners/ProductionRebalancerRunner.js.map +1 -0
- package/dist/runners/SimpleRunner.d.ts +31 -0
- package/dist/runners/SimpleRunner.d.ts.map +1 -0
- package/dist/runners/SimpleRunner.js +286 -0
- package/dist/runners/SimpleRunner.js.map +1 -0
- package/dist/runners/SimulationRegistry.d.ts +46 -0
- package/dist/runners/SimulationRegistry.d.ts.map +1 -0
- package/dist/runners/SimulationRegistry.js +156 -0
- package/dist/runners/SimulationRegistry.js.map +1 -0
- package/dist/types.d.ts +637 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +158 -0
- package/dist/types.js.map +1 -0
- package/dist/visualizer/HtmlTimelineGenerator.d.ts +6 -0
- package/dist/visualizer/HtmlTimelineGenerator.d.ts.map +1 -0
- package/dist/visualizer/HtmlTimelineGenerator.js +1321 -0
- package/dist/visualizer/HtmlTimelineGenerator.js.map +1 -0
- package/dist/visualizer/index.d.ts +4 -0
- package/dist/visualizer/index.d.ts.map +1 -0
- package/dist/visualizer/index.js +3 -0
- package/dist/visualizer/index.js.map +1 -0
- package/package.json +62 -0
- package/src/BridgeMockController.ts +404 -0
- package/src/KPICollector.ts +304 -0
- package/src/MessageTracker.ts +312 -0
- package/src/RebalancerSimulationHarness.ts +325 -0
- package/src/ScenarioGenerator.ts +433 -0
- package/src/ScenarioLoader.ts +73 -0
- package/src/SimulationDeployment.ts +265 -0
- package/src/SimulationEngine.ts +432 -0
- package/src/index.ts +101 -0
- package/src/runners/NoOpRebalancer.ts +40 -0
- package/src/runners/ProductionRebalancerRunner.ts +289 -0
- package/src/runners/SimpleRunner.ts +382 -0
- package/src/runners/SimulationRegistry.ts +215 -0
- package/src/types.ts +878 -0
- package/src/visualizer/HtmlTimelineGenerator.ts +1341 -0
- package/src/visualizer/index.ts +7 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { ERC20Test__factory, MockValueTransferBridge__factory, } from '@hyperlane-xyz/core';
|
|
4
|
+
import { rootLogger } from '@hyperlane-xyz/utils';
|
|
5
|
+
import { DEFAULT_BRIDGE_ROUTE_CONFIG } from './types.js';
|
|
6
|
+
const logger = rootLogger.child({ module: 'BridgeMockController' });
|
|
7
|
+
/**
|
|
8
|
+
* BridgeMockController manages simulated bridge transfers with configurable
|
|
9
|
+
* delays, failures, and fees. It intercepts SentTransferRemote events and
|
|
10
|
+
* schedules async delivery to simulate real bridge behavior.
|
|
11
|
+
*/
|
|
12
|
+
export class BridgeMockController extends EventEmitter {
|
|
13
|
+
provider;
|
|
14
|
+
domains;
|
|
15
|
+
deployerKey;
|
|
16
|
+
bridgeConfig;
|
|
17
|
+
pendingTransfers = new Map();
|
|
18
|
+
completedTransfers = [];
|
|
19
|
+
transferCounter = 0;
|
|
20
|
+
deliveryTimers = new Map();
|
|
21
|
+
isRunning = false;
|
|
22
|
+
eventListeners = new Map();
|
|
23
|
+
// Transaction queue to prevent nonce collisions
|
|
24
|
+
txQueue = [];
|
|
25
|
+
txProcessing = false;
|
|
26
|
+
constructor(provider, domains, deployerKey, bridgeConfig = {}) {
|
|
27
|
+
super();
|
|
28
|
+
this.provider = provider;
|
|
29
|
+
this.domains = domains;
|
|
30
|
+
this.deployerKey = deployerKey;
|
|
31
|
+
this.bridgeConfig = bridgeConfig;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Queue a transaction to be executed serially (prevents nonce collisions)
|
|
35
|
+
*/
|
|
36
|
+
async queueTransaction(fn) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
this.txQueue.push(async () => {
|
|
39
|
+
try {
|
|
40
|
+
await fn();
|
|
41
|
+
resolve();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
reject(error);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
void this.processQueue();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Process queued transactions one at a time
|
|
52
|
+
*/
|
|
53
|
+
async processQueue() {
|
|
54
|
+
if (this.txProcessing || this.txQueue.length === 0)
|
|
55
|
+
return;
|
|
56
|
+
this.txProcessing = true;
|
|
57
|
+
while (this.txQueue.length > 0) {
|
|
58
|
+
const fn = this.txQueue.shift();
|
|
59
|
+
if (fn) {
|
|
60
|
+
try {
|
|
61
|
+
await fn();
|
|
62
|
+
}
|
|
63
|
+
catch (_error) {
|
|
64
|
+
// Error already handled in queueTransaction
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.txProcessing = false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Gets the bridge config for a specific route
|
|
72
|
+
*/
|
|
73
|
+
getRouteConfig(origin, destination) {
|
|
74
|
+
return (this.bridgeConfig[origin]?.[destination] ?? DEFAULT_BRIDGE_ROUTE_CONFIG);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Calculates delivery delay with jitter
|
|
78
|
+
*/
|
|
79
|
+
calculateDelay(config) {
|
|
80
|
+
const jitter = (Math.random() - 0.5) * 2 * config.deliveryJitter;
|
|
81
|
+
return Math.max(0, config.deliveryDelay + jitter);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Start listening for bridge events
|
|
85
|
+
*/
|
|
86
|
+
async start() {
|
|
87
|
+
if (this.isRunning)
|
|
88
|
+
return;
|
|
89
|
+
this.isRunning = true;
|
|
90
|
+
const deployer = new ethers.Wallet(this.deployerKey, this.provider);
|
|
91
|
+
// Set up event listeners for each bridge
|
|
92
|
+
for (const [chainName, domain] of Object.entries(this.domains)) {
|
|
93
|
+
const bridge = MockValueTransferBridge__factory.connect(domain.bridge, deployer);
|
|
94
|
+
// Listen for SentTransferRemote events
|
|
95
|
+
bridge.on(bridge.filters.SentTransferRemote(), (origin, destination, recipient, amount) => {
|
|
96
|
+
void this.onTransferInitiated(chainName, origin, destination, recipient, amount.toBigInt());
|
|
97
|
+
});
|
|
98
|
+
this.eventListeners.set(chainName, bridge);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Stop listening and cancel pending deliveries
|
|
103
|
+
*/
|
|
104
|
+
async stop() {
|
|
105
|
+
this.isRunning = false;
|
|
106
|
+
// Remove event listeners
|
|
107
|
+
for (const bridge of this.eventListeners.values()) {
|
|
108
|
+
bridge.removeAllListeners();
|
|
109
|
+
}
|
|
110
|
+
this.eventListeners.clear();
|
|
111
|
+
// Cancel pending delivery timers
|
|
112
|
+
for (const timer of this.deliveryTimers.values()) {
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
}
|
|
115
|
+
this.deliveryTimers.clear();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Handle transfer initiated event
|
|
119
|
+
*/
|
|
120
|
+
async onTransferInitiated(originChain, originDomainId, destinationDomainId, recipientBytes32, amount) {
|
|
121
|
+
// Find destination chain by domain ID
|
|
122
|
+
const destChain = Object.entries(this.domains).find(([_, d]) => d.domainId === destinationDomainId)?.[0];
|
|
123
|
+
if (!destChain) {
|
|
124
|
+
logger.error({ destinationDomainId }, 'Unknown destination domain');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const config = this.getRouteConfig(originChain, destChain);
|
|
128
|
+
const transferId = `bridge-${this.transferCounter++}`;
|
|
129
|
+
const delay = this.calculateDelay(config);
|
|
130
|
+
// Apply token fee if configured
|
|
131
|
+
let netAmount = amount;
|
|
132
|
+
if (config.tokenFeeBps) {
|
|
133
|
+
const fee = (amount * BigInt(config.tokenFeeBps)) / BigInt(10000);
|
|
134
|
+
netAmount = amount - fee;
|
|
135
|
+
}
|
|
136
|
+
const recipient = ethers.utils.hexDataSlice(recipientBytes32, 12);
|
|
137
|
+
// MockValueTransferBridge pulls tokens from origin warp token.
|
|
138
|
+
// Bridge delivery mints to destination, preserving total warp token collateral.
|
|
139
|
+
const pendingTransfer = {
|
|
140
|
+
id: transferId,
|
|
141
|
+
origin: originChain,
|
|
142
|
+
destination: destChain,
|
|
143
|
+
amount: netAmount,
|
|
144
|
+
recipient,
|
|
145
|
+
scheduledDelivery: Date.now() + delay,
|
|
146
|
+
failed: false,
|
|
147
|
+
delivered: false,
|
|
148
|
+
};
|
|
149
|
+
this.pendingTransfers.set(transferId, pendingTransfer);
|
|
150
|
+
// Emit event
|
|
151
|
+
const bridgeEvent = {
|
|
152
|
+
type: 'transfer_initiated',
|
|
153
|
+
transfer: pendingTransfer,
|
|
154
|
+
timestamp: Date.now(),
|
|
155
|
+
};
|
|
156
|
+
this.emit('transfer_initiated', bridgeEvent);
|
|
157
|
+
// Schedule delivery
|
|
158
|
+
const timer = setTimeout(() => this.executeDelivery(transferId, config), delay);
|
|
159
|
+
this.deliveryTimers.set(transferId, timer);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Execute delivery of a pending transfer
|
|
163
|
+
*/
|
|
164
|
+
async executeDelivery(transferId, config) {
|
|
165
|
+
const transfer = this.pendingTransfers.get(transferId);
|
|
166
|
+
if (!transfer || transfer.delivered)
|
|
167
|
+
return;
|
|
168
|
+
this.deliveryTimers.delete(transferId);
|
|
169
|
+
// Check for failure
|
|
170
|
+
if (Math.random() < config.failureRate) {
|
|
171
|
+
transfer.failed = true;
|
|
172
|
+
this.pendingTransfers.delete(transferId);
|
|
173
|
+
this.completedTransfers.push(transfer);
|
|
174
|
+
const event = {
|
|
175
|
+
type: 'transfer_failed',
|
|
176
|
+
transfer,
|
|
177
|
+
timestamp: Date.now(),
|
|
178
|
+
};
|
|
179
|
+
this.emit('transfer_failed', event);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
// Execute the delivery by simulating tokens arriving at destination
|
|
184
|
+
// In a real scenario, this would call the destination warp token's handle function
|
|
185
|
+
// For simulation, we directly transfer tokens to simulate bridge completion
|
|
186
|
+
await this.simulateBridgeDelivery(transfer);
|
|
187
|
+
transfer.delivered = true;
|
|
188
|
+
transfer.deliveredAt = Date.now();
|
|
189
|
+
this.pendingTransfers.delete(transferId);
|
|
190
|
+
this.completedTransfers.push(transfer);
|
|
191
|
+
const event = {
|
|
192
|
+
type: 'transfer_delivered',
|
|
193
|
+
transfer,
|
|
194
|
+
timestamp: Date.now(),
|
|
195
|
+
};
|
|
196
|
+
this.emit('transfer_delivered', event);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
logger.error({ transferId, error }, 'Bridge delivery failed');
|
|
200
|
+
transfer.failed = true;
|
|
201
|
+
this.pendingTransfers.delete(transferId);
|
|
202
|
+
this.completedTransfers.push(transfer);
|
|
203
|
+
const event = {
|
|
204
|
+
type: 'transfer_failed',
|
|
205
|
+
transfer,
|
|
206
|
+
timestamp: Date.now(),
|
|
207
|
+
};
|
|
208
|
+
this.emit('transfer_failed', event);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Simulate bridge delivery by burning from origin bridge and minting to destination.
|
|
213
|
+
* This maintains token conservation across the simulation.
|
|
214
|
+
* Uses transaction queue to prevent nonce collisions.
|
|
215
|
+
*/
|
|
216
|
+
async simulateBridgeDelivery(transfer) {
|
|
217
|
+
await this.queueTransaction(async () => {
|
|
218
|
+
const deployer = new ethers.Wallet(this.deployerKey, this.provider);
|
|
219
|
+
const originDomain = this.domains[transfer.origin];
|
|
220
|
+
const destDomain = this.domains[transfer.destination];
|
|
221
|
+
// Burn from origin bridge (tokens are guaranteed to be there since SentTransferRemote fired)
|
|
222
|
+
const originCollateralToken = ERC20Test__factory.connect(originDomain.collateralToken, deployer);
|
|
223
|
+
const burnTx = await originCollateralToken.burnFrom(originDomain.bridge, transfer.amount.toString());
|
|
224
|
+
await burnTx.wait();
|
|
225
|
+
// Mint same amount to destination warp token
|
|
226
|
+
const destCollateralToken = ERC20Test__factory.connect(destDomain.collateralToken, deployer);
|
|
227
|
+
const mintTx = await destCollateralToken.mintTo(destDomain.warpToken, transfer.amount.toString());
|
|
228
|
+
await mintTx.wait();
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Manually trigger delivery for a pending transfer (for testing)
|
|
233
|
+
*/
|
|
234
|
+
async forceDelivery(transferId) {
|
|
235
|
+
const transfer = this.pendingTransfers.get(transferId);
|
|
236
|
+
if (!transfer) {
|
|
237
|
+
throw new Error(`Transfer not found: ${transferId}`);
|
|
238
|
+
}
|
|
239
|
+
// Cancel scheduled delivery
|
|
240
|
+
const timer = this.deliveryTimers.get(transferId);
|
|
241
|
+
if (timer) {
|
|
242
|
+
clearTimeout(timer);
|
|
243
|
+
this.deliveryTimers.delete(transferId);
|
|
244
|
+
}
|
|
245
|
+
// Execute immediately
|
|
246
|
+
await this.executeDelivery(transferId, this.getRouteConfig(transfer.origin, transfer.destination));
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Check if there are pending transfers
|
|
250
|
+
*/
|
|
251
|
+
hasPendingTransfers() {
|
|
252
|
+
return this.pendingTransfers.size > 0;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get count of pending transfers
|
|
256
|
+
*/
|
|
257
|
+
getPendingCount() {
|
|
258
|
+
return this.pendingTransfers.size;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get all pending transfers
|
|
262
|
+
*/
|
|
263
|
+
getPendingTransfers() {
|
|
264
|
+
return Array.from(this.pendingTransfers.values());
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Get completed transfers
|
|
268
|
+
*/
|
|
269
|
+
getCompletedTransfers() {
|
|
270
|
+
return [...this.completedTransfers];
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Wait for all pending transfers to complete
|
|
274
|
+
* On timeout, marks remaining transfers as failed and clears them
|
|
275
|
+
*/
|
|
276
|
+
async waitForAllDeliveries(timeoutMs = 30000) {
|
|
277
|
+
const startTime = Date.now();
|
|
278
|
+
while (this.hasPendingTransfers()) {
|
|
279
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
280
|
+
const pendingCount = this.getPendingCount();
|
|
281
|
+
logger.warn({ pendingCount }, 'Timeout waiting for bridge deliveries - marking as failed');
|
|
282
|
+
// Mark all pending as failed, update state, and clear
|
|
283
|
+
for (const transfer of this.pendingTransfers.values()) {
|
|
284
|
+
transfer.failed = true;
|
|
285
|
+
this.completedTransfers.push(transfer);
|
|
286
|
+
const event = {
|
|
287
|
+
type: 'transfer_failed',
|
|
288
|
+
transfer,
|
|
289
|
+
timestamp: Date.now(),
|
|
290
|
+
};
|
|
291
|
+
this.emit('transfer_failed', event);
|
|
292
|
+
}
|
|
293
|
+
this.pendingTransfers.clear();
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=BridgeMockController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BridgeMockController.js","sourceRoot":"","sources":["../src/BridgeMockController.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { ethers } from 'ethers';
|
|
2
|
+
import type { DeployedDomain, RebalanceRecord, SimulationKPIs, TransferRecord } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* KPICollector tracks metrics throughout a simulation run.
|
|
5
|
+
*/
|
|
6
|
+
export declare class KPICollector {
|
|
7
|
+
private readonly provider;
|
|
8
|
+
private readonly domains;
|
|
9
|
+
private transferRecords;
|
|
10
|
+
private rebalanceRecords;
|
|
11
|
+
/** Maps bridge transfer ID to rebalance ID for correlation */
|
|
12
|
+
private bridgeToRebalanceMap;
|
|
13
|
+
private initialBalances;
|
|
14
|
+
constructor(provider: ethers.providers.JsonRpcProvider, domains: Record<string, DeployedDomain>);
|
|
15
|
+
/**
|
|
16
|
+
* Initialize with initial balances (passed explicitly or fetched)
|
|
17
|
+
*/
|
|
18
|
+
initialize(initialBalances?: Record<string, bigint>): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Get current balance for a chain's warp token
|
|
21
|
+
*/
|
|
22
|
+
private getBalance;
|
|
23
|
+
/**
|
|
24
|
+
* Record transfer start
|
|
25
|
+
*/
|
|
26
|
+
recordTransferStart(id: string, origin: string, destination: string, amount: bigint): void;
|
|
27
|
+
/**
|
|
28
|
+
* Record transfer completion
|
|
29
|
+
*/
|
|
30
|
+
recordTransferComplete(id: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Record transfer failure
|
|
33
|
+
*/
|
|
34
|
+
recordTransferFailed(id: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Mark all pending transfers as complete (used after mailbox processing)
|
|
37
|
+
*/
|
|
38
|
+
markAllPendingAsComplete(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Record a rebalance operation start (when SentTransferRemote fires)
|
|
41
|
+
* Returns the rebalance ID for correlation
|
|
42
|
+
*/
|
|
43
|
+
recordRebalanceStart(origin: string, destination: string, amount: bigint, gasCost: bigint): string;
|
|
44
|
+
/**
|
|
45
|
+
* Link a bridge transfer ID to a rebalance ID for delivery tracking
|
|
46
|
+
*/
|
|
47
|
+
linkBridgeTransfer(bridgeTransferId: string, rebalanceId: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Record rebalance completion (when bridge delivers)
|
|
50
|
+
*/
|
|
51
|
+
recordRebalanceComplete(bridgeTransferId: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Record rebalance failure
|
|
54
|
+
*/
|
|
55
|
+
recordRebalanceFailed(bridgeTransferId: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get pending rebalances count
|
|
58
|
+
*/
|
|
59
|
+
getPendingRebalancesCount(): number;
|
|
60
|
+
/**
|
|
61
|
+
* Calculate percentile from sorted array
|
|
62
|
+
*/
|
|
63
|
+
private percentile;
|
|
64
|
+
/**
|
|
65
|
+
* Generate final KPIs
|
|
66
|
+
*/
|
|
67
|
+
generateKPIs(): Promise<SimulationKPIs>;
|
|
68
|
+
/**
|
|
69
|
+
* Get transfer records
|
|
70
|
+
*/
|
|
71
|
+
getTransferRecords(): TransferRecord[];
|
|
72
|
+
/**
|
|
73
|
+
* Get rebalance records
|
|
74
|
+
*/
|
|
75
|
+
getRebalanceRecords(): RebalanceRecord[];
|
|
76
|
+
/**
|
|
77
|
+
* Reset collector for new simulation
|
|
78
|
+
*/
|
|
79
|
+
reset(): void;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=KPICollector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KPICollector.d.ts","sourceRoot":"","sources":["../src/KPICollector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIrC,OAAO,KAAK,EAEV,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,qBAAa,YAAY;IAQrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR1B,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,gBAAgB,CAA2C;IACnE,8DAA8D;IAC9D,OAAO,CAAC,oBAAoB,CAAkC;IAC9D,OAAO,CAAC,eAAe,CAA8B;gBAGlC,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,eAAe,EAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;IAG1D;;OAEG;IACG,UAAU,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzE;;OAEG;YACW,UAAU;IAUxB;;OAEG;IACH,mBAAmB,CACjB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACb,IAAI;IAWP;;OAEG;IACH,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IASxC;;OAEG;IACH,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQtC;;OAEG;IACH,wBAAwB,IAAI,IAAI;IAWhC;;;OAGG;IACH,oBAAoB,CAClB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,MAAM;IAcT;;OAEG;IACH,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAQvE;;OAEG;IACH,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAYvD;;OAEG;IACH,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAWrD;;OAEG;IACH,yBAAyB,IAAI,MAAM;IAMnC;;OAEG;IACH,OAAO,CAAC,UAAU;IAMlB;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC;IAuF7C;;OAEG;IACH,kBAAkB,IAAI,cAAc,EAAE;IAItC;;OAEG;IACH,mBAAmB,IAAI,eAAe,EAAE;IAIxC;;OAEG;IACH,KAAK,IAAI,IAAI;CAMd"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { ERC20Test__factory } from '@hyperlane-xyz/core';
|
|
2
|
+
/**
|
|
3
|
+
* KPICollector tracks metrics throughout a simulation run.
|
|
4
|
+
*/
|
|
5
|
+
export class KPICollector {
|
|
6
|
+
provider;
|
|
7
|
+
domains;
|
|
8
|
+
transferRecords = new Map();
|
|
9
|
+
rebalanceRecords = new Map();
|
|
10
|
+
/** Maps bridge transfer ID to rebalance ID for correlation */
|
|
11
|
+
bridgeToRebalanceMap = new Map();
|
|
12
|
+
initialBalances = {};
|
|
13
|
+
constructor(provider, domains) {
|
|
14
|
+
this.provider = provider;
|
|
15
|
+
this.domains = domains;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize with initial balances (passed explicitly or fetched)
|
|
19
|
+
*/
|
|
20
|
+
async initialize(initialBalances) {
|
|
21
|
+
if (initialBalances) {
|
|
22
|
+
this.initialBalances = { ...initialBalances };
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
for (const chainName of Object.keys(this.domains)) {
|
|
26
|
+
this.initialBalances[chainName] = await this.getBalance(chainName);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get current balance for a chain's warp token
|
|
32
|
+
*/
|
|
33
|
+
async getBalance(chainName) {
|
|
34
|
+
const domain = this.domains[chainName];
|
|
35
|
+
const token = ERC20Test__factory.connect(domain.collateralToken, this.provider);
|
|
36
|
+
const balance = await token.balanceOf(domain.warpToken);
|
|
37
|
+
return balance.toBigInt();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Record transfer start
|
|
41
|
+
*/
|
|
42
|
+
recordTransferStart(id, origin, destination, amount) {
|
|
43
|
+
this.transferRecords.set(id, {
|
|
44
|
+
id,
|
|
45
|
+
origin,
|
|
46
|
+
destination,
|
|
47
|
+
amount,
|
|
48
|
+
startTime: Date.now(),
|
|
49
|
+
status: 'pending',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Record transfer completion
|
|
54
|
+
*/
|
|
55
|
+
recordTransferComplete(id) {
|
|
56
|
+
const record = this.transferRecords.get(id);
|
|
57
|
+
if (record) {
|
|
58
|
+
record.endTime = Date.now();
|
|
59
|
+
record.latency = record.endTime - record.startTime;
|
|
60
|
+
record.status = 'completed';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Record transfer failure
|
|
65
|
+
*/
|
|
66
|
+
recordTransferFailed(id) {
|
|
67
|
+
const record = this.transferRecords.get(id);
|
|
68
|
+
if (record) {
|
|
69
|
+
record.endTime = Date.now();
|
|
70
|
+
record.status = 'failed';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Mark all pending transfers as complete (used after mailbox processing)
|
|
75
|
+
*/
|
|
76
|
+
markAllPendingAsComplete() {
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
for (const record of this.transferRecords.values()) {
|
|
79
|
+
if (record.status === 'pending') {
|
|
80
|
+
record.endTime = now;
|
|
81
|
+
record.latency = now - record.startTime;
|
|
82
|
+
record.status = 'completed';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Record a rebalance operation start (when SentTransferRemote fires)
|
|
88
|
+
* Returns the rebalance ID for correlation
|
|
89
|
+
*/
|
|
90
|
+
recordRebalanceStart(origin, destination, amount, gasCost) {
|
|
91
|
+
const id = `rebalance-${this.rebalanceRecords.size}`;
|
|
92
|
+
this.rebalanceRecords.set(id, {
|
|
93
|
+
id,
|
|
94
|
+
origin,
|
|
95
|
+
destination,
|
|
96
|
+
amount,
|
|
97
|
+
startTime: Date.now(),
|
|
98
|
+
gasCost,
|
|
99
|
+
status: 'pending',
|
|
100
|
+
});
|
|
101
|
+
return id;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Link a bridge transfer ID to a rebalance ID for delivery tracking
|
|
105
|
+
*/
|
|
106
|
+
linkBridgeTransfer(bridgeTransferId, rebalanceId) {
|
|
107
|
+
this.bridgeToRebalanceMap.set(bridgeTransferId, rebalanceId);
|
|
108
|
+
const record = this.rebalanceRecords.get(rebalanceId);
|
|
109
|
+
if (record) {
|
|
110
|
+
record.bridgeTransferId = bridgeTransferId;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Record rebalance completion (when bridge delivers)
|
|
115
|
+
*/
|
|
116
|
+
recordRebalanceComplete(bridgeTransferId) {
|
|
117
|
+
const rebalanceId = this.bridgeToRebalanceMap.get(bridgeTransferId);
|
|
118
|
+
if (!rebalanceId)
|
|
119
|
+
return;
|
|
120
|
+
const record = this.rebalanceRecords.get(rebalanceId);
|
|
121
|
+
if (record && record.status === 'pending') {
|
|
122
|
+
record.endTime = Date.now();
|
|
123
|
+
record.latency = record.endTime - record.startTime;
|
|
124
|
+
record.status = 'completed';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Record rebalance failure
|
|
129
|
+
*/
|
|
130
|
+
recordRebalanceFailed(bridgeTransferId) {
|
|
131
|
+
const rebalanceId = this.bridgeToRebalanceMap.get(bridgeTransferId);
|
|
132
|
+
if (!rebalanceId)
|
|
133
|
+
return;
|
|
134
|
+
const record = this.rebalanceRecords.get(rebalanceId);
|
|
135
|
+
if (record) {
|
|
136
|
+
record.endTime = Date.now();
|
|
137
|
+
record.status = 'failed';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get pending rebalances count
|
|
142
|
+
*/
|
|
143
|
+
getPendingRebalancesCount() {
|
|
144
|
+
return Array.from(this.rebalanceRecords.values()).filter((r) => r.status === 'pending').length;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Calculate percentile from sorted array
|
|
148
|
+
*/
|
|
149
|
+
percentile(sorted, p) {
|
|
150
|
+
if (sorted.length === 0)
|
|
151
|
+
return 0;
|
|
152
|
+
const index = Math.ceil((p / 100) * sorted.length) - 1;
|
|
153
|
+
return sorted[Math.max(0, index)];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Generate final KPIs
|
|
157
|
+
*/
|
|
158
|
+
async generateKPIs() {
|
|
159
|
+
const transfers = Array.from(this.transferRecords.values());
|
|
160
|
+
const completed = transfers.filter((t) => t.status === 'completed');
|
|
161
|
+
const failed = transfers.filter((t) => t.status === 'failed');
|
|
162
|
+
// Calculate latencies
|
|
163
|
+
const latencies = completed
|
|
164
|
+
.filter((t) => t.latency !== undefined)
|
|
165
|
+
.map((t) => t.latency)
|
|
166
|
+
.sort((a, b) => a - b);
|
|
167
|
+
const avgLatency = latencies.length > 0
|
|
168
|
+
? latencies.reduce((a, b) => a + b, 0) / latencies.length
|
|
169
|
+
: 0;
|
|
170
|
+
// Calculate per-chain metrics
|
|
171
|
+
const perChainMetrics = {};
|
|
172
|
+
for (const chainName of Object.keys(this.domains)) {
|
|
173
|
+
const transfersIn = transfers.filter((t) => t.destination === chainName && t.status === 'completed').length;
|
|
174
|
+
const transfersOut = transfers.filter((t) => t.origin === chainName && t.status === 'completed').length;
|
|
175
|
+
const allRebalances = Array.from(this.rebalanceRecords.values());
|
|
176
|
+
const rebalancesIn = allRebalances.filter((r) => r.destination === chainName && r.status === 'completed').length;
|
|
177
|
+
const rebalancesOut = allRebalances.filter((r) => r.origin === chainName && r.status === 'completed').length;
|
|
178
|
+
const rebalanceVolumeIn = allRebalances
|
|
179
|
+
.filter((r) => r.destination === chainName && r.status === 'completed')
|
|
180
|
+
.reduce((sum, r) => sum + r.amount, BigInt(0));
|
|
181
|
+
const rebalanceVolumeOut = allRebalances
|
|
182
|
+
.filter((r) => r.origin === chainName && r.status === 'completed')
|
|
183
|
+
.reduce((sum, r) => sum + r.amount, BigInt(0));
|
|
184
|
+
const finalBalance = await this.getBalance(chainName);
|
|
185
|
+
perChainMetrics[chainName] = {
|
|
186
|
+
chainName,
|
|
187
|
+
initialBalance: this.initialBalances[chainName] ?? BigInt(0),
|
|
188
|
+
finalBalance,
|
|
189
|
+
transfersIn,
|
|
190
|
+
transfersOut,
|
|
191
|
+
rebalancesIn,
|
|
192
|
+
rebalancesOut,
|
|
193
|
+
rebalanceVolumeIn,
|
|
194
|
+
rebalanceVolumeOut,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// Calculate rebalance totals
|
|
198
|
+
const allRebalanceRecords = Array.from(this.rebalanceRecords.values());
|
|
199
|
+
const completedRebalances = allRebalanceRecords.filter((r) => r.status === 'completed');
|
|
200
|
+
const totalRebalanceVolume = completedRebalances.reduce((sum, r) => sum + r.amount, BigInt(0));
|
|
201
|
+
const totalGasCost = completedRebalances.reduce((sum, r) => sum + r.gasCost, BigInt(0));
|
|
202
|
+
return {
|
|
203
|
+
totalTransfers: transfers.length,
|
|
204
|
+
completedTransfers: completed.length,
|
|
205
|
+
failedTransfers: failed.length,
|
|
206
|
+
completionRate: transfers.length > 0 ? completed.length / transfers.length : 1,
|
|
207
|
+
averageLatency: avgLatency,
|
|
208
|
+
p50Latency: this.percentile(latencies, 50),
|
|
209
|
+
p95Latency: this.percentile(latencies, 95),
|
|
210
|
+
p99Latency: this.percentile(latencies, 99),
|
|
211
|
+
totalRebalances: completedRebalances.length,
|
|
212
|
+
rebalanceVolume: totalRebalanceVolume,
|
|
213
|
+
totalGasCost,
|
|
214
|
+
perChainMetrics,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get transfer records
|
|
219
|
+
*/
|
|
220
|
+
getTransferRecords() {
|
|
221
|
+
return Array.from(this.transferRecords.values());
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get rebalance records
|
|
225
|
+
*/
|
|
226
|
+
getRebalanceRecords() {
|
|
227
|
+
return Array.from(this.rebalanceRecords.values());
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Reset collector for new simulation
|
|
231
|
+
*/
|
|
232
|
+
reset() {
|
|
233
|
+
this.transferRecords.clear();
|
|
234
|
+
this.rebalanceRecords.clear();
|
|
235
|
+
this.bridgeToRebalanceMap.clear();
|
|
236
|
+
this.initialBalances = {};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=KPICollector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KPICollector.js","sourceRoot":"","sources":["../src/KPICollector.ts"],"names":[],"mappings":"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"}
|