Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
332 changes: 173 additions & 159 deletions README.md

Large diffs are not rendered by default.

124 changes: 118 additions & 6 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/.tsbuildinfo

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from './managers';
export * from './types';
export * from './utils';
export * from './stores';
export * from './new';
//# sourceMappingURL=index.d.ts.map
2 changes: 1 addition & 1 deletion dist/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

390 changes: 257 additions & 133 deletions dist/index.js

Large diffs are not rendered by default.

387 changes: 254 additions & 133 deletions dist/index.mjs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions dist/new/helpers/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './profiler';
//# sourceMappingURL=index.d.ts.map
1 change: 1 addition & 0 deletions dist/new/helpers/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions dist/new/helpers/profiler.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
type ProfilerOptions = {
profileDir?: string;
intervalMs?: number;
};
export declare class Profiler {
private session;
private profileDir;
private intervalMs;
private intervalHandle;
private snapshotCount;
constructor(options?: ProfilerOptions);
private post;
private saveSnapshot;
start(): Promise<void>;
stop(): void;
}
export {};
//# sourceMappingURL=profiler.d.ts.map
1 change: 1 addition & 0 deletions dist/new/helpers/profiler.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions dist/new/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './managers';
export * from './helpers';
export * from './types';
//# sourceMappingURL=index.d.ts.map
1 change: 1 addition & 0 deletions dist/new/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions dist/new/managers/balance.manager.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ViemClientManager } from '../../managers';
import { ConceroNetwork } from '../../types';
import { ConceroChain } from '../types';
export interface IBalanceManagerSender {
send: (options: {
chain: ConceroChain;
network: ConceroNetwork;
expectedBalance: bigint;
actualBalance: bigint;
}) => Promise<void>;
}
type Options = {
pollingInterval?: number;
gasLimit?: number;
actionsCount?: number;
viemClientManager: ViemClientManager;
sender: IBalanceManagerSender;
};
export declare class NewBalanceManager {
private readonly _gasLimit;
private readonly _actionsCount;
private readonly _pollingInterval;
private readonly _viemClientManager;
private readonly _sender;
private _networks;
private _chains;
constructor(options: Options);
setNetworks(networks: ConceroNetwork[]): Promise<void>;
setChains(chains: Record<ConceroChain['name'], ConceroChain>): Promise<void>;
startPolling(): Promise<void>;
private processNetwork;
}
export {};
//# sourceMappingURL=balance.manager.d.ts.map
1 change: 1 addition & 0 deletions dist/new/managers/balance.manager.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions dist/new/managers/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './balance.manager';
//# sourceMappingURL=index.d.ts.map
1 change: 1 addition & 0 deletions dist/new/managers/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions dist/new/types/chain.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export declare enum ConceroChainDeploymentType {
Router = "router",
ValidatorLib = "validatorLib",
RelayerLib = "relayerLib"
}
export type ConceroChain = {
id: string;
chainSelector: number;
name: string;
isTestnet: boolean;
finalityTagEnabled: boolean;
finalityConfirmations: number;
minBlockConfirmations: number;
rpcUrls: string[];
blockExplorers: {
name: string;
url: string;
apiUrl: string;
}[];
nativeCurrency: {
name: string;
symbol: string;
decimals: number;
};
deployments: Partial<Record<ConceroChainDeploymentType, `0x${string}`>>;
};
//# sourceMappingURL=chain.d.ts.map
1 change: 1 addition & 0 deletions dist/new/types/chain.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions dist/new/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './chain';
//# sourceMappingURL=index.d.ts.map
1 change: 1 addition & 0 deletions dist/new/types/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/utils/generateUid.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*
* @example
* ```typescript
* import { generateUid } from '../utils/generateUid';
* import { generateUid } from '../helpers/generateUid';
*
* const watcherId = generateUid();
* const subscriberId = generateUid();
Expand Down
2 changes: 1 addition & 1 deletion dist/utils/generateUid.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "4.7.0",
"@types/jest": "30.0.0",
"@types/node": "24.1.0",
"@types/node": "^25.0.3",
"@typescript-eslint/eslint-plugin": "8.39.1",
"@typescript-eslint/parser": "8.39.1",
"esbuild": "0.25.8",
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ export * from './utils';

//Export all stores
export * from './stores';


export * from './new';
1 change: 1 addition & 0 deletions src/new/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './profiler';
88 changes: 88 additions & 0 deletions src/new/helpers/profiler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import fs from 'fs';
import inspector from 'inspector';
import path from 'path';

type ProfilerOptions = {
profileDir?: string; // Folder to save profiling files
intervalMs?: number; // Interval between snapshots in milliseconds
};

export class Profiler {
private session: inspector.Session;
private profileDir: string;
private intervalMs: number;
private intervalHandle: NodeJS.Timeout | null = null;
private snapshotCount = 0;

constructor(options: ProfilerOptions = {}) {
this.profileDir = options.profileDir ?? './profiles';
this.intervalMs = options.intervalMs ?? 5 * 60 * 1000; // Default 5 minutes

// Create directory if it doesn't exist
if (!fs.existsSync(this.profileDir)) {
fs.mkdirSync(this.profileDir, { recursive: true });
}

// Initialize inspector session
this.session = new inspector.Session();
this.session.connect();
}

// Helper function to send commands to the inspector
private post<T = any>(method: string, params: Record<string, any> = {}): Promise<T> {
return new Promise((resolve, reject) => {
this.session.post(method, params, (err, result) => {
if (err) reject(err);
else resolve(result as T);
});
});
}

// Save CPU and HEAP snapshots
private async saveSnapshot() {
this.snapshotCount++;
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const cpuFile = path.join(this.profileDir, `cpu_${timestamp}.cpuprofile`);
const heapFile = path.join(this.profileDir, `heap_${timestamp}.heapsnapshot`);

// Stop CPU profiler, get the snapshot
const { profile } = await this.post<{ profile: inspector.Profiler.Profile }>(
'Profiler.stop',
);
fs.writeFileSync(cpuFile, JSON.stringify(profile));

// HEAP snapshot — write chunks directly to file to save memory
const heapStream = fs.createWriteStream(heapFile);
const onChunk = (m: any) => heapStream.write(m.params.chunk);
this.session.on('HeapProfiler.addHeapSnapshotChunk', onChunk);
await this.post('HeapProfiler.takeHeapSnapshot', { reportProgress: false });
this.session.removeListener('HeapProfiler.addHeapSnapshotChunk', onChunk);
heapStream.end();

console.log(`Saved snapshot #${this.snapshotCount}: ${cpuFile} + ${heapFile}`);

// Restart CPU profiler for the next interval
await this.post('Profiler.start');
}

// Start the eternal profiling loop
public async start() {
await this.post('Profiler.enable');
await this.post('HeapProfiler.enable');
await this.post('Profiler.start');

// Schedule periodic snapshots
this.intervalHandle = setInterval(() => this.saveSnapshot(), this.intervalMs);
console.log(`EternalProfiler started. Snapshot every ${this.intervalMs / 1000}s`);
}

// Stop the profiler and disconnect session
public stop() {
if (this.intervalHandle) {
clearInterval(this.intervalHandle);
this.intervalHandle = null;
console.log('EternalProfiler stopped.');
}
this.session.disconnect();
}
}
3 changes: 3 additions & 0 deletions src/new/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './managers';
export * from './helpers';
export * from './types';
84 changes: 84 additions & 0 deletions src/new/managers/balance.manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { zeroAddress } from 'viem';

import { ViemClientManager } from '../../managers';
import { ConceroNetwork } from '../../types';
import { ConceroChain } from '../types';

export interface IBalanceManagerSender {
send: (options: {
chain: ConceroChain;
network: ConceroNetwork;
expectedBalance: bigint;
actualBalance: bigint;
}) => Promise<void>;
}
type Options = {
pollingInterval?: number;
gasLimit?: number;
actionsCount?: number;
viemClientManager: ViemClientManager;
sender: IBalanceManagerSender;
};

export class NewBalanceManager {
private readonly _gasLimit: number;
private readonly _actionsCount: number;
private readonly _pollingInterval: number;
private readonly _viemClientManager: ViemClientManager;
private readonly _sender: IBalanceManagerSender;

private _networks: ConceroNetwork[] = [];
private _chains: Record<ConceroChain['name'], ConceroChain> = {};

constructor(options: Options) {
this._gasLimit = options.gasLimit ?? 300_000;
this._actionsCount = options.actionsCount ?? 100;
this._pollingInterval = options.pollingInterval ?? 30 * 60_000;
this._viemClientManager = options.viemClientManager;
this._sender = options.sender;
}

async setNetworks(networks: ConceroNetwork[]) {
this._networks = networks;
}

async setChains(chains: Record<ConceroChain['name'], ConceroChain>) {
this._chains = chains;
}

async startPolling(): Promise<void> {
setTimeout(async () => {
await Promise.all(this._networks.map(this.processNetwork));
}, this._pollingInterval);
}

private async processNetwork(network: ConceroNetwork) {
try {
const chain = this._chains[network.name];

const viemClients = this._viemClientManager.getClients(network.name);
const [baseFee, actualBalance] = await Promise.all([
viemClients.publicClient.getBlobBaseFee(),
viemClients.publicClient.getBalance({
address: zeroAddress,
}),
]);

const expectedBalance =
(baseFee / BigInt(Math.pow(10, chain.nativeCurrency.decimals))) *
BigInt(this._gasLimit) *
BigInt(this._actionsCount);

if (expectedBalance < actualBalance) {
await this._sender.send({
chain,
actualBalance,
expectedBalance,
network,
});
}
} catch (e) {
} finally {
}
}
}
1 change: 1 addition & 0 deletions src/new/managers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './balance.manager';
27 changes: 27 additions & 0 deletions src/new/types/chain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export enum ConceroChainDeploymentType {
Router = 'router',
ValidatorLib = 'validatorLib',
RelayerLib = 'relayerLib',
}

export type ConceroChain = {
id: string;
chainSelector: number;
name: string;
isTestnet: boolean;
finalityTagEnabled: boolean;
finalityConfirmations: number;
minBlockConfirmations: number;
rpcUrls: string[];
blockExplorers: {
name: string;
url: string;
apiUrl: string;
}[];
nativeCurrency: {
name: string;
symbol: string;
decimals: number;
};
deployments: Partial<Record<ConceroChainDeploymentType, `0x${string}`>>;
};
1 change: 1 addition & 0 deletions src/new/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './chain';
6 changes: 5 additions & 1 deletion src/utils/generateUid.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { v4 as uuidv4 } from 'uuid';





/**
* Generates a unique identifier using UUID v4.
*
Expand All @@ -10,7 +14,7 @@ import { v4 as uuidv4 } from 'uuid';
*
* @example
* ```typescript
* import { generateUid } from '../utils/generateUid';
* import { generateUid } from '../helpers/generateUid';
*
* const watcherId = generateUid();
* const subscriberId = generateUid();
Expand Down
Loading