@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.
Files changed (83) hide show
  1. package/LICENSE.md +195 -0
  2. package/README.md +582 -0
  3. package/dist/BridgeMockController.d.ts +87 -0
  4. package/dist/BridgeMockController.d.ts.map +1 -0
  5. package/dist/BridgeMockController.js +300 -0
  6. package/dist/BridgeMockController.js.map +1 -0
  7. package/dist/KPICollector.d.ts +81 -0
  8. package/dist/KPICollector.d.ts.map +1 -0
  9. package/dist/KPICollector.js +239 -0
  10. package/dist/KPICollector.js.map +1 -0
  11. package/dist/MessageTracker.d.ts +82 -0
  12. package/dist/MessageTracker.d.ts.map +1 -0
  13. package/dist/MessageTracker.js +213 -0
  14. package/dist/MessageTracker.js.map +1 -0
  15. package/dist/RebalancerSimulationHarness.d.ts +72 -0
  16. package/dist/RebalancerSimulationHarness.d.ts.map +1 -0
  17. package/dist/RebalancerSimulationHarness.js +217 -0
  18. package/dist/RebalancerSimulationHarness.js.map +1 -0
  19. package/dist/ScenarioGenerator.d.ts +50 -0
  20. package/dist/ScenarioGenerator.d.ts.map +1 -0
  21. package/dist/ScenarioGenerator.js +326 -0
  22. package/dist/ScenarioGenerator.js.map +1 -0
  23. package/dist/ScenarioLoader.d.ts +18 -0
  24. package/dist/ScenarioLoader.d.ts.map +1 -0
  25. package/dist/ScenarioLoader.js +59 -0
  26. package/dist/ScenarioLoader.js.map +1 -0
  27. package/dist/SimulationDeployment.d.ts +20 -0
  28. package/dist/SimulationDeployment.d.ts.map +1 -0
  29. package/dist/SimulationDeployment.js +170 -0
  30. package/dist/SimulationDeployment.js.map +1 -0
  31. package/dist/SimulationEngine.d.ts +58 -0
  32. package/dist/SimulationEngine.d.ts.map +1 -0
  33. package/dist/SimulationEngine.js +302 -0
  34. package/dist/SimulationEngine.js.map +1 -0
  35. package/dist/index.d.ts +22 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +26 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/runners/NoOpRebalancer.d.ts +17 -0
  40. package/dist/runners/NoOpRebalancer.d.ts.map +1 -0
  41. package/dist/runners/NoOpRebalancer.js +28 -0
  42. package/dist/runners/NoOpRebalancer.js.map +1 -0
  43. package/dist/runners/ProductionRebalancerRunner.d.ts +22 -0
  44. package/dist/runners/ProductionRebalancerRunner.d.ts.map +1 -0
  45. package/dist/runners/ProductionRebalancerRunner.js +219 -0
  46. package/dist/runners/ProductionRebalancerRunner.js.map +1 -0
  47. package/dist/runners/SimpleRunner.d.ts +31 -0
  48. package/dist/runners/SimpleRunner.d.ts.map +1 -0
  49. package/dist/runners/SimpleRunner.js +286 -0
  50. package/dist/runners/SimpleRunner.js.map +1 -0
  51. package/dist/runners/SimulationRegistry.d.ts +46 -0
  52. package/dist/runners/SimulationRegistry.d.ts.map +1 -0
  53. package/dist/runners/SimulationRegistry.js +156 -0
  54. package/dist/runners/SimulationRegistry.js.map +1 -0
  55. package/dist/types.d.ts +637 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +158 -0
  58. package/dist/types.js.map +1 -0
  59. package/dist/visualizer/HtmlTimelineGenerator.d.ts +6 -0
  60. package/dist/visualizer/HtmlTimelineGenerator.d.ts.map +1 -0
  61. package/dist/visualizer/HtmlTimelineGenerator.js +1321 -0
  62. package/dist/visualizer/HtmlTimelineGenerator.js.map +1 -0
  63. package/dist/visualizer/index.d.ts +4 -0
  64. package/dist/visualizer/index.d.ts.map +1 -0
  65. package/dist/visualizer/index.js +3 -0
  66. package/dist/visualizer/index.js.map +1 -0
  67. package/package.json +62 -0
  68. package/src/BridgeMockController.ts +404 -0
  69. package/src/KPICollector.ts +304 -0
  70. package/src/MessageTracker.ts +312 -0
  71. package/src/RebalancerSimulationHarness.ts +325 -0
  72. package/src/ScenarioGenerator.ts +433 -0
  73. package/src/ScenarioLoader.ts +73 -0
  74. package/src/SimulationDeployment.ts +265 -0
  75. package/src/SimulationEngine.ts +432 -0
  76. package/src/index.ts +101 -0
  77. package/src/runners/NoOpRebalancer.ts +40 -0
  78. package/src/runners/ProductionRebalancerRunner.ts +289 -0
  79. package/src/runners/SimpleRunner.ts +382 -0
  80. package/src/runners/SimulationRegistry.ts +215 -0
  81. package/src/types.ts +878 -0
  82. package/src/visualizer/HtmlTimelineGenerator.ts +1341 -0
  83. 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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"}